use core::{
any::{self, Any, TypeId},
ptr::NonNull,
};
use crate::{
handlers::{ContextFormattingStyle, ContextHandler, FormattingFunction},
report::{
data::ReportData,
raw::{RawReport, RawReportMut, RawReportRef},
},
util::Erased,
};
pub(crate) struct ReportVtable {
type_id: fn() -> TypeId,
type_name: fn() -> &'static str,
handler_type_id: fn() -> TypeId,
drop: unsafe fn(NonNull<ReportData<Erased>>),
clone_arc: unsafe fn(NonNull<ReportData<Erased>>) -> RawReport,
strong_count: unsafe fn(RawReportRef<'_>) -> usize,
source: unsafe fn(RawReportRef<'_>) -> Option<&(dyn core::error::Error + 'static)>,
display: unsafe fn(RawReportRef<'_>, &mut core::fmt::Formatter<'_>) -> core::fmt::Result,
debug: unsafe fn(RawReportRef<'_>, &mut core::fmt::Formatter<'_>) -> core::fmt::Result,
preferred_context_formatting_style:
unsafe fn(RawReportRef<'_>, FormattingFunction) -> ContextFormattingStyle,
context_as_any: unsafe fn(RawReportRef<'_>) -> &(dyn Any + 'static),
context_as_any_mut: unsafe fn(RawReportMut<'_>) -> &mut (dyn Any + 'static),
}
impl ReportVtable {
pub(super) const fn new<C: 'static, H: ContextHandler<C>>() -> &'static Self {
const {
&Self {
type_id: TypeId::of::<C>,
type_name: any::type_name::<C>,
handler_type_id: TypeId::of::<H>,
drop: drop::<C>,
clone_arc: clone_arc::<C>,
strong_count: strong_count::<C>,
source: source::<C, H>,
display: display::<C, H>,
debug: debug::<C, H>,
preferred_context_formatting_style: preferred_context_formatting_style::<C, H>,
context_as_any: context_as_any::<C>,
context_as_any_mut: context_as_any_mut::<C>,
}
}
}
#[inline]
pub(super) fn type_id(&self) -> TypeId {
(self.type_id)()
}
#[inline]
pub(super) fn type_name(&self) -> &'static str {
(self.type_name)()
}
#[inline]
pub(super) fn handler_type_id(&self) -> TypeId {
(self.handler_type_id)()
}
#[inline]
pub(super) unsafe fn drop(&self, ptr: NonNull<ReportData<Erased>>) {
unsafe {
(self.drop)(ptr);
}
}
#[inline]
pub(super) unsafe fn clone_arc(&self, ptr: NonNull<ReportData<Erased>>) -> RawReport {
unsafe {
(self.clone_arc)(ptr)
}
}
#[inline]
pub(super) unsafe fn strong_count<'a>(&self, ptr: RawReportRef<'a>) -> usize {
unsafe {
(self.strong_count)(ptr)
}
}
#[inline]
pub(super) unsafe fn source<'a>(
&self,
ptr: RawReportRef<'a>,
) -> Option<&'a (dyn core::error::Error + 'static)> {
unsafe {
(self.source)(ptr)
}
}
#[inline]
pub(super) unsafe fn display(
&self,
ptr: RawReportRef<'_>,
formatter: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
unsafe {
(self.display)(ptr, formatter)
}
}
#[inline]
pub(super) unsafe fn debug(
&self,
ptr: RawReportRef<'_>,
formatter: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
unsafe {
(self.debug)(ptr, formatter)
}
}
#[inline]
pub(super) unsafe fn preferred_context_formatting_style(
&self,
ptr: RawReportRef<'_>,
report_formatting_function: FormattingFunction,
) -> ContextFormattingStyle {
unsafe {
(self.preferred_context_formatting_style)(ptr, report_formatting_function)
}
}
#[inline]
pub(super) unsafe fn context_as_any<'a>(
&self,
ptr: RawReportRef<'a>,
) -> &'a (dyn Any + 'static) {
unsafe {
(self.context_as_any)(ptr)
}
}
#[inline]
pub(super) unsafe fn context_as_any_mut<'a>(
&self,
ptr: RawReportMut<'a>,
) -> &'a mut (dyn Any + 'static) {
unsafe {
(self.context_as_any_mut)(ptr)
}
}
}
pub(super) unsafe fn drop<C: 'static>(ptr: NonNull<ReportData<Erased>>) {
let ptr: NonNull<ReportData<C>> = ptr.cast();
let ptr = ptr.as_ptr();
let arc = unsafe {
triomphe::Arc::from_raw(ptr)
};
core::mem::drop(arc);
}
unsafe fn clone_arc<C: 'static>(ptr: NonNull<ReportData<Erased>>) -> RawReport {
let ptr: *const ReportData<C> = ptr.cast::<ReportData<C>>().as_ptr();
let arc_borrow = unsafe {
triomphe::ArcBorrow::from_ptr(ptr)
};
let arc = arc_borrow.clone_arc();
RawReport::from_arc(arc)
}
unsafe fn strong_count<'a, C: 'static>(ptr: RawReportRef<'a>) -> usize {
let ptr: *const ReportData<C> = ptr.as_ptr().cast::<ReportData<C>>();
let arc_borrow = unsafe {
triomphe::ArcBorrow::from_ptr(ptr)
};
triomphe::ArcBorrow::strong_count(&arc_borrow)
}
unsafe fn source<'a, C: 'static, H: ContextHandler<C>>(
ptr: RawReportRef<'a>,
) -> Option<&'a (dyn core::error::Error + 'static)> {
let context: &C = unsafe { ptr.context_downcast_unchecked::<C>() };
H::source(context)
}
unsafe fn display<C: 'static, H: ContextHandler<C>>(
ptr: RawReportRef<'_>,
formatter: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
let context: &C = unsafe { ptr.context_downcast_unchecked::<C>() };
H::display(context, formatter)
}
unsafe fn debug<C: 'static, H: ContextHandler<C>>(
ptr: RawReportRef<'_>,
formatter: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
let context: &C = unsafe { ptr.context_downcast_unchecked::<C>() };
H::debug(context, formatter)
}
unsafe fn preferred_context_formatting_style<C: 'static, H: ContextHandler<C>>(
ptr: RawReportRef<'_>,
report_formatting_function: FormattingFunction,
) -> ContextFormattingStyle {
let context: &C = unsafe { ptr.context_downcast_unchecked::<C>() };
H::preferred_formatting_style(context, report_formatting_function)
}
unsafe fn context_as_any<'a, C: 'static>(ptr: RawReportRef<'a>) -> &'a (dyn Any + 'static) {
let context: &C = unsafe { ptr.context_downcast_unchecked::<C>() };
context as &(dyn Any + 'static)
}
unsafe fn context_as_any_mut<'a, C: 'static>(ptr: RawReportMut<'a>) -> &'a mut (dyn Any + 'static) {
let context: &mut C = unsafe { ptr.into_context_downcast_unchecked::<C>() };
context as &mut (dyn Any + 'static)
}
#[cfg(test)]
mod tests {
use alloc::vec;
use core::{error::Error, fmt};
use super::*;
use crate::{handlers::ContextHandler, report::RawReport};
struct HandlerI32;
impl ContextHandler<i32> for HandlerI32 {
fn source(_value: &i32) -> Option<&(dyn Error + 'static)> {
None
}
fn display(value: &i32, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(value, formatter)
}
fn debug(value: &i32, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(value, formatter)
}
}
#[test]
fn test_report_vtable_eq() {
let vtable1 = ReportVtable::new::<i32, HandlerI32>();
let vtable2 = ReportVtable::new::<i32, HandlerI32>();
assert!(core::ptr::eq(vtable1, vtable2));
}
#[test]
fn test_report_type_id() {
let vtable = ReportVtable::new::<i32, HandlerI32>();
assert_eq!(vtable.type_id(), TypeId::of::<i32>());
}
#[test]
fn test_report_type_name() {
let vtable = ReportVtable::new::<i32, HandlerI32>();
assert_eq!(vtable.type_name(), core::any::type_name::<i32>());
}
#[test]
fn test_report_clone_eq() {
let report = RawReport::new::<_, HandlerI32>(42, vec![], vec![]);
let cloned_report = unsafe { report.as_ref().clone_arc() };
assert!(core::ptr::eq(
report.as_ref().as_ptr(),
cloned_report.as_ref().as_ptr()
));
}
}