#![allow(clippy::missing_const_for_fn)]
use std::{
error::Error as ErrorTrait,
fmt::{self, Debug, Display},
marker::PhantomData,
mem,
};
#[allow(unused_imports)]
use core_extensions::SelfOps;
use crate::{
erased_types::{
c_functions::{adapt_std_fmt, debug_impl, display_impl},
FormattingMode,
},
marker_type::{ErasedObject, SyncSend, UnsyncSend, UnsyncUnsend},
pointer_trait::{AsMutPtr, AsPtr},
prefix_type::WithMetadata,
sabi_types::RRef,
std_types::{
utypeid::{new_utypeid, UTypeId},
RBox, ROption, RResult, RStr, RString,
},
utils::transmute_reference,
};
#[cfg(test)]
mod test;
#[repr(C)]
#[derive(StableAbi)]
pub struct RBoxError_<M = SyncSend> {
value: RBox<ErasedObject>,
vtable: RErrorVTable_Ref,
_sync_send: PhantomData<M>,
}
pub type UnsyncRBoxError = RBoxError_<UnsyncUnsend>;
pub type SendRBoxError = RBoxError_<UnsyncSend>;
pub type RBoxError = RBoxError_<SyncSend>;
impl RBoxError_<SyncSend> {
pub fn new<T>(value: T) -> Self
where
T: ErrorTrait + Send + Sync + 'static,
{
Self::new_inner(value)
}
}
impl RBoxError_<UnsyncSend> {
pub fn new<T>(value: T) -> Self
where
T: ErrorTrait + Send + 'static,
{
Self::new_inner(value)
}
}
impl RBoxError_<UnsyncUnsend> {
pub fn new<T>(value: T) -> Self
where
T: ErrorTrait + 'static,
{
Self::new_inner(value)
}
}
impl<M> RBoxError_<M> {
pub fn from_fmt<T>(value: &T) -> Self
where
T: Display + Debug + ?Sized,
{
DebugDisplay {
debug: format!("{:#?}", value),
display: format!("{:#}", value),
}
.piped(Self::from_debug_display)
}
pub fn from_debug<T>(value: &T) -> Self
where
T: Debug + ?Sized,
{
DebugDisplay {
debug: format!("{:#?}", value),
display: format!("{:#?}", value),
}
.piped(Self::from_debug_display)
}
fn from_debug_display(value: DebugDisplay) -> Self {
unsafe { Self::new_with_vtable(value, MakeRErrorVTable::LIB_VTABLE_DEBUG_DISPLAY) }
}
fn new_inner<T>(value: T) -> Self
where
T: ErrorTrait + 'static,
{
unsafe { Self::new_with_vtable(value, MakeRErrorVTable::<T>::LIB_VTABLE) }
}
unsafe fn new_with_vtable<T>(value: T, vtable: RErrorVTable_Ref) -> Self {
let value = value
.piped(RBox::new)
.piped(|x| unsafe { mem::transmute::<RBox<T>, RBox<ErasedObject>>(x) });
Self {
value,
vtable,
_sync_send: PhantomData,
}
}
}
impl<M> RBoxError_<M> {
pub fn to_formatted_error<N>(&self) -> RBoxError_<N> {
if let Some(dd) = self.as_debug_display() {
RBoxError_::from_debug_display(DebugDisplay {
debug: dd.debug.into(),
display: dd.display.into(),
})
} else {
RBoxError_::from_fmt(self)
}
}
fn as_debug_display(&self) -> Option<DebugDisplayRef<'_>> {
unsafe { self.vtable.as_debug_display()(self.value.as_rref()).into_option() }
}
}
impl<M> RBoxError_<M> {
pub fn type_id(&self) -> UTypeId {
self.vtable.type_id()()
}
fn is_type<T: 'static>(&self) -> bool {
let self_id = self.vtable.type_id()();
let other_id = UTypeId::new::<T>();
self_id == other_id
}
pub fn heap_address(&self) -> usize {
(self.value.as_ptr()) as *const _ as usize
}
pub fn as_unsync(&self) -> &UnsyncRBoxError {
unsafe { transmute_reference::<RBoxError_<M>, UnsyncRBoxError>(self) }
}
pub fn into_unsync(self) -> UnsyncRBoxError {
unsafe { mem::transmute::<RBoxError_<M>, UnsyncRBoxError>(self) }
}
}
impl RBoxError_<SyncSend> {
pub fn as_send(&self) -> &SendRBoxError {
unsafe { transmute_reference::<RBoxError_<SyncSend>, SendRBoxError>(self) }
}
pub fn into_send(self) -> SendRBoxError {
unsafe { mem::transmute::<RBoxError_<SyncSend>, SendRBoxError>(self) }
}
}
impl<M> ErrorTrait for RBoxError_<M> {}
impl<M> Display for RBoxError_<M> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
unsafe { adapt_std_fmt(self.value.as_rref(), self.vtable.display(), f) }
}
}
impl<M> Debug for RBoxError_<M> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
unsafe { adapt_std_fmt(self.value.as_rref(), self.vtable.debug(), f) }
}
}
macro_rules! from_impls {
(
$first_docs: expr,
$boxdyn: ty,
$marker: ty,
) => {
impl From<$boxdyn> for RBoxError_<$marker> {
#[doc = $first_docs]
fn from(this: $boxdyn) -> RBoxError_<$marker> {
Self::from_box(this)
}
}
impl RBoxError_<$marker> {
#[doc = $first_docs]
pub fn from_box(this: $boxdyn) -> Self {
match this.downcast::<Self>() {
Ok(e) => *e,
Err(e) => unsafe {
Self::new_with_vtable::<$boxdyn>(
e,
MakeBoxedRErrorVTable::<$boxdyn>::LIB_VTABLE,
)
},
}
}
pub fn into_box(self) -> $boxdyn {
if self.is_type::<$boxdyn>() {
unsafe {
let box_ = mem::transmute::<RBox<ErasedObject>, RBox<$boxdyn>>(self.value);
RBox::into_inner(box_)
}
} else {
Box::new(self)
}
}
pub fn downcast<T>(self) -> Result<RBox<T>, Self>
where
T: ErrorTrait + 'static,
{
match (self.is_type::<T>(), self.is_type::<$boxdyn>()) {
(true, _) => unsafe {
Ok(mem::transmute::<RBox<ErasedObject>, RBox<T>>(self.value))
},
(false, true) if self.downcast_ref::<T>().is_some() => unsafe {
let x = mem::transmute::<RBox<ErasedObject>, RBox<$boxdyn>>(self.value);
let x = RBox::into_inner(x);
Ok(RBox::from_box(x.downcast::<T>().unwrap()))
},
(false, _) => Err(self),
}
}
pub fn downcast_ref<T>(&self) -> Option<&T>
where
T: ErrorTrait + 'static,
{
match (self.is_type::<T>(), self.is_type::<$boxdyn>()) {
(true, _) => unsafe { Some(&*(self.value.as_ptr() as *const T)) },
(false, true) => unsafe {
let ref_box = &*(self.value.as_ptr() as *const $boxdyn);
(&**ref_box).downcast_ref::<T>()
},
(false, false) => None,
}
}
pub fn downcast_mut<T>(&mut self) -> Option<&mut T>
where
T: ErrorTrait + 'static,
{
match (self.is_type::<T>(), self.is_type::<$boxdyn>()) {
(true, _) => unsafe { Some(&mut *(self.value.as_mut_ptr() as *mut T)) },
(false, true) => unsafe {
let mut_box = &mut *(self.value.as_mut_ptr() as *mut $boxdyn);
(&mut **mut_box).downcast_mut::<T>()
},
(false, false) => None,
}
}
}
};
}
from_impls! {
"Converts a `Box<dyn Error + Send + Sync>` to a `Send + Sync` `RBoxError_`.",
Box<dyn ErrorTrait + Send + Sync + 'static> ,
SyncSend,
}
from_impls! {
"Converts a `Box<dyn Error + Send>` to a `Send + !Sync` `RBoxError_`.",
Box<dyn ErrorTrait + Send + 'static> ,
UnsyncSend,
}
from_impls! {
"Converts a `Box<dyn Error>` to a `!Send + !Sync` `RBoxError_`.",
Box<dyn ErrorTrait + 'static> ,
UnsyncUnsend,
}
#[repr(C)]
#[derive(StableAbi)]
#[sabi(kind(Prefix))]
struct RErrorVTable {
debug: unsafe extern "C" fn(
RRef<'_, ErasedObject>,
FormattingMode,
&mut RString,
) -> RResult<(), ()>,
display: unsafe extern "C" fn(
RRef<'_, ErasedObject>,
FormattingMode,
&mut RString,
) -> RResult<(), ()>,
as_debug_display: unsafe extern "C" fn(RRef<'_, ErasedObject>) -> ROption<DebugDisplayRef<'_>>,
#[sabi(last_prefix_field)]
type_id: extern "C" fn() -> UTypeId,
}
struct MakeRErrorVTable<T>(T);
impl<T> MakeRErrorVTable<T>
where
T: ErrorTrait + 'static,
{
const VALUE: RErrorVTable = RErrorVTable {
debug: debug_impl::<T>,
display: display_impl::<T>,
as_debug_display: not_as_debug_display,
type_id: new_utypeid::<T>,
};
const VALUE_MD: &'static WithMetadata<RErrorVTable> = &WithMetadata::new(Self::VALUE);
const LIB_VTABLE: RErrorVTable_Ref = { RErrorVTable_Ref(Self::VALUE_MD.static_as_prefix()) };
}
impl MakeRErrorVTable<DebugDisplay> {
const WM_DEBUG_DISPLAY: &'static WithMetadata<RErrorVTable> = {
&WithMetadata::new(RErrorVTable {
debug: debug_impl::<DebugDisplay>,
display: display_impl::<DebugDisplay>,
as_debug_display,
type_id: new_utypeid::<DebugDisplay>,
})
};
const LIB_VTABLE_DEBUG_DISPLAY: RErrorVTable_Ref =
{ RErrorVTable_Ref(Self::WM_DEBUG_DISPLAY.static_as_prefix()) };
}
struct MakeBoxedRErrorVTable<T>(T);
impl<T> MakeBoxedRErrorVTable<Box<T>>
where
T: ?Sized + ErrorTrait + 'static,
{
const VALUE: RErrorVTable = RErrorVTable {
debug: debug_impl::<Box<T>>,
display: display_impl::<Box<T>>,
as_debug_display: not_as_debug_display,
type_id: new_utypeid::<Box<T>>,
};
const WM_VTABLE: &'static WithMetadata<RErrorVTable> = &WithMetadata::new(Self::VALUE);
const LIB_VTABLE: RErrorVTable_Ref = RErrorVTable_Ref(Self::WM_VTABLE.static_as_prefix());
}
#[repr(C)]
#[derive(Clone)]
struct DebugDisplay {
debug: String,
display: String,
}
impl Display for DebugDisplay {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.display, f)
}
}
impl Debug for DebugDisplay {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.debug, f)
}
}
impl ErrorTrait for DebugDisplay {}
#[repr(C)]
#[derive(Debug, StableAbi, PartialEq)]
struct DebugDisplayRef<'a> {
debug: RStr<'a>,
display: RStr<'a>,
}
unsafe extern "C" fn as_debug_display(
this: RRef<'_, ErasedObject>,
) -> ROption<DebugDisplayRef<'_>> {
extern_fn_panic_handling! {
let this = unsafe{ this.transmute_into_ref::<DebugDisplay>() };
ROption::RSome(DebugDisplayRef{
debug: this.debug.as_str().into(),
display: this.display.as_str().into(),
})
}
}
unsafe extern "C" fn not_as_debug_display(
_: RRef<'_, ErasedObject>,
) -> ROption<DebugDisplayRef<'_>> {
ROption::RNone
}