use crate::error::ptr::{MutPtr, OwnedPtr, SharedPtr}; use crate::error::{ConcreteError, DynError, Error, ErrorExt, OutOfMemory}; use core::{any::TypeId, fmt, ptr::NonNull}; use std_alloc::boxed::Box; /// A vtable containing the `ErrorExt` methods for some type `T`. /// /// This is used to create thin-pointer equivalents of `Box`, /// `&dyn ErrorExt`, and `&mut ErrorExt`, which would all otherwise be two words /// in size. /// /// # Safety /// /// The safety contract for all vtable functions is the same: /// /// * `SharedPtr<'_, DynError>`s must be valid for reading a `ConcreteError`, /// `MutPtr<'_, DynError>`s must additionally be valid for writing a /// `ConcreteError`, and `OwnedPtr`s must additionally be valid /// to deallocate with `ConcreteError`'s layout. /// /// * If a `OomOrDynError{Ref,Mut}` return value contains a `{Shared,Mut}Ptr<'_, /// DynError>`, it must be valid for reading (and, in the case of `MutPtr`, /// writing) `DynError`s. #[repr(C)] pub(crate) struct Vtable { pub(crate) display: unsafe fn(SharedPtr<'_, DynError>, &mut fmt::Formatter<'_>) -> fmt::Result, pub(crate) debug: unsafe fn(SharedPtr<'_, DynError>, &mut fmt::Formatter<'_>) -> fmt::Result, pub(crate) source: unsafe fn(SharedPtr<'_, DynError>) -> Option<&Error>, pub(crate) source_mut: unsafe fn(MutPtr<'_, DynError>) -> Option<&mut Error>, pub(crate) is: unsafe fn(SharedPtr<'_, DynError>, TypeId) -> bool, pub(crate) as_dyn_core_error: unsafe fn(SharedPtr<'_, DynError>) -> &(dyn core::error::Error + Send + Sync + 'static), pub(crate) into_boxed_dyn_core_error: unsafe fn( OwnedPtr, ) -> Result, OutOfMemory>, pub(crate) drop_and_deallocate: unsafe fn(OwnedPtr), /// Additional safety requirement: the `NonNull` pointer must be valid /// for writing a `T`. /// /// Upon successful return, a `T` will have been written to that memory /// block. pub(crate) move_into: unsafe fn(OwnedPtr, NonNull), /// Conversion into `anyhow::Error` from `Box`. #[cfg(feature = "anyhow")] pub(crate) into_anyhow: unsafe fn(OwnedPtr) -> anyhow::Error, } impl Vtable { /// Get the `Vtable` of the `E: ErrorExt` type parameter. pub(crate) fn of() -> &'static Self where E: ErrorExt, { &Vtable { display: display::, debug: debug::, source: source::, source_mut: source_mut::, is: is::, as_dyn_core_error: as_dyn_core_error::, into_boxed_dyn_core_error: into_boxed_dyn_core_error::, drop_and_deallocate: drop_and_deallocate::, move_into: move_into::, #[cfg(feature = "anyhow")] into_anyhow: into_anyhow::, } } } unsafe fn display(error: SharedPtr<'_, DynError>, f: &mut fmt::Formatter<'_>) -> fmt::Result where E: ErrorExt, { let error = error.cast::>(); // Safety: implied by all vtable functions' safety contract. let error = unsafe { error.as_ref() }; fmt::Display::fmt(error.error.ext_as_dyn_core_error(), f) } unsafe fn debug(error: SharedPtr<'_, DynError>, f: &mut fmt::Formatter<'_>) -> fmt::Result where E: ErrorExt, { let error = error.cast::>(); // Safety: implied by all vtable functions' safety contract. let error = unsafe { error.as_ref() }; fmt::Debug::fmt(error.error.ext_as_dyn_core_error(), f) } unsafe fn source(error: SharedPtr<'_, DynError>) -> Option<&Error> where E: ErrorExt, { let error = error.cast::>(); // Safety: implied by all vtable functions' safety contract. let error = unsafe { error.as_ref() }; error.error.ext_source() } unsafe fn source_mut(error: MutPtr<'_, DynError>) -> Option<&mut Error> where E: ErrorExt, { let mut error = error.cast::>(); // Safety: implied by all vtable functions' safety contract. let error = unsafe { error.as_mut() }; error.error.ext_source_mut() } unsafe fn is(error: SharedPtr<'_, DynError>, type_id: TypeId) -> bool where E: ErrorExt, { let error = error.cast::>(); // Safety: implied by all vtable functions' safety contract. let error = unsafe { error.as_ref() }; error.error.ext_is(type_id) } unsafe fn as_dyn_core_error( error: SharedPtr<'_, DynError>, ) -> &(dyn core::error::Error + Send + Sync + 'static) where E: ErrorExt, { let error = error.cast::>(); // Safety: implied by all vtable functions' safety contract. let error = unsafe { error.as_ref() }; error.error.ext_as_dyn_core_error() } unsafe fn into_boxed_dyn_core_error( error: OwnedPtr, ) -> Result, OutOfMemory> where E: ErrorExt, { let error = error.cast::>(); // Safety: implied by all vtable functions' safety contract. let error = unsafe { error.into_box() }; error.error.ext_into_boxed_dyn_core_error() } unsafe fn drop_and_deallocate(error: OwnedPtr) where E: ErrorExt, { let error = error.cast::>(); // Safety: implied by all vtable functions' safety contract. let _ = unsafe { error.into_box() }; } unsafe fn move_into(error: OwnedPtr, ret_ptr: NonNull) where E: ErrorExt, { let error = error.cast::>(); // Safety: implied by all vtable functions' safety contract. let error = unsafe { error.into_box() }; // Safety: Implied by `move`'s additional safety safety requirement. unsafe { error.error.ext_move(ret_ptr); } } #[cfg(feature = "anyhow")] unsafe fn into_anyhow(error: OwnedPtr) -> anyhow::Error where E: ErrorExt, { let error = error.cast::>(); // Safety: implied by all vtable functions' safety contract. let error = unsafe { error.into_box() }; error.error.ext_into_anyhow() }