use alloc::vec::Vec;
use core::{
any::{Any, TypeId},
ptr::NonNull,
};
use crate::{
attachment::RawAttachment,
handlers::{ContextFormattingStyle, ContextHandler, FormattingFunction},
report::data::ReportData,
util::Erased,
};
#[repr(transparent)]
pub struct RawReport {
ptr: NonNull<ReportData<Erased>>,
}
impl RawReport {
#[inline]
pub(super) fn from_arc<C: 'static>(data: triomphe::Arc<ReportData<C>>) -> Self {
let ptr: *const ReportData<C> = triomphe::Arc::into_raw(data);
let ptr: *mut ReportData<Erased> = ptr.cast::<ReportData<Erased>>().cast_mut();
let ptr: NonNull<ReportData<Erased>> = unsafe { NonNull::new_unchecked(ptr) };
Self {
ptr,
}
}
#[inline]
pub(super) fn into_non_null(self) -> NonNull<ReportData<Erased>> {
let ptr = self.ptr;
core::mem::forget(self);
ptr
}
#[inline]
pub fn new<C, H>(context: C, children: Vec<RawReport>, attachments: Vec<RawAttachment>) -> Self
where
C: 'static,
H: ContextHandler<C>,
{
let data = triomphe::Arc::new(ReportData::new::<H>(context, children, attachments));
Self::from_arc(data)
}
#[inline]
pub fn as_ref(&self) -> RawReportRef<'_> {
RawReportRef {
ptr: self.ptr,
_marker: core::marker::PhantomData,
}
}
#[inline]
pub unsafe fn as_mut(&mut self) -> RawReportMut<'_> {
RawReportMut {
ptr: self.ptr,
_marker: core::marker::PhantomData,
}
}
}
impl core::ops::Drop for RawReport {
#[inline]
fn drop(&mut self) {
let vtable = self.as_ref().vtable();
unsafe {
vtable.drop(self.ptr);
}
}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct RawReportRef<'a> {
ptr: NonNull<ReportData<Erased>>,
_marker: core::marker::PhantomData<&'a ReportData<Erased>>,
}
impl<'a> RawReportRef<'a> {
#[inline]
pub(super) unsafe fn cast_inner<C>(self) -> &'a ReportData<C> {
debug_assert_eq!(self.vtable().type_id(), TypeId::of::<C>());
let this = self.ptr.cast::<ReportData<C>>();
unsafe { this.as_ref() }
}
#[inline]
pub(super) fn as_ptr(self) -> *const ReportData<Erased> {
self.ptr.as_ptr()
}
#[inline]
pub fn context_type_id(self) -> TypeId {
self.vtable().type_id()
}
#[inline]
pub fn context_type_name(self) -> &'static str {
self.vtable().type_name()
}
#[inline]
pub fn context_handler_type_id(self) -> TypeId {
self.vtable().handler_type_id()
}
#[inline]
pub fn context_source(self) -> Option<&'a (dyn core::error::Error + 'static)> {
let vtable = self.vtable();
unsafe { vtable.source(self) }
}
#[inline]
pub fn context_display(self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let vtable = self.vtable();
unsafe { vtable.display(self, formatter) }
}
#[inline]
pub fn context_debug(self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let vtable = self.vtable();
unsafe { vtable.debug(self, formatter) }
}
#[inline]
pub fn preferred_context_formatting_style(
self,
report_formatting_function: FormattingFunction,
) -> ContextFormattingStyle {
let vtable = self.vtable();
unsafe {
vtable.preferred_context_formatting_style(self, report_formatting_function)
}
}
#[inline]
pub unsafe fn clone_arc(self) -> RawReport {
let vtable = self.vtable();
unsafe {
vtable.clone_arc(self.ptr)
}
}
#[inline]
pub fn context_as_any(self) -> &'a (dyn Any + 'static) {
let vtable = self.vtable();
unsafe {
vtable.context_as_any(self)
}
}
#[inline]
pub fn strong_count(self) -> usize {
let vtable = self.vtable();
unsafe {
vtable.strong_count(self)
}
}
}
#[repr(transparent)]
pub struct RawReportMut<'a> {
ptr: NonNull<ReportData<Erased>>,
_marker: core::marker::PhantomData<&'a mut ReportData<Erased>>,
}
impl<'a> RawReportMut<'a> {
#[inline]
pub(super) unsafe fn cast_inner<C>(self) -> &'a mut ReportData<C> {
debug_assert_eq!(self.as_ref().vtable().type_id(), TypeId::of::<C>());
let mut this = self.ptr.cast::<ReportData<C>>();
unsafe { this.as_mut() }
}
#[inline]
pub fn reborrow<'b>(&'b mut self) -> RawReportMut<'b> {
RawReportMut {
ptr: self.ptr,
_marker: core::marker::PhantomData,
}
}
#[inline]
pub fn as_ref(&self) -> RawReportRef<'_> {
RawReportRef {
ptr: self.ptr,
_marker: core::marker::PhantomData,
}
}
#[inline]
pub fn into_ref(self) -> RawReportRef<'a> {
RawReportRef {
ptr: self.ptr,
_marker: core::marker::PhantomData,
}
}
#[inline]
pub(super) fn into_mut_ptr(self) -> *mut ReportData<Erased> {
self.ptr.as_ptr()
}
#[inline]
pub fn into_context_as_any_mut(self) -> &'a mut (dyn Any + 'static) {
let vtable = self.as_ref().vtable();
unsafe {
vtable.context_as_any_mut(self)
}
}
}
#[cfg(test)]
mod tests {
use alloc::{string::String, vec};
use core::{error::Error, fmt};
use super::*;
use crate::handlers::ContextHandler;
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)
}
}
struct HandlerString;
impl ContextHandler<String> for HandlerString {
fn source(_value: &String) -> Option<&(dyn Error + 'static)> {
None
}
fn display(value: &String, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(value, formatter)
}
fn debug(value: &String, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(value, formatter)
}
}
#[test]
fn test_raw_report_size() {
assert_eq!(
core::mem::size_of::<RawReport>(),
core::mem::size_of::<usize>()
);
assert_eq!(
core::mem::size_of::<Option<RawReport>>(),
core::mem::size_of::<usize>()
);
assert_eq!(
core::mem::size_of::<Result<(), RawReport>>(),
core::mem::size_of::<usize>()
);
assert_eq!(
core::mem::size_of::<Result<String, RawReport>>(),
core::mem::size_of::<String>()
);
assert_eq!(
core::mem::size_of::<Option<Option<RawReport>>>(),
core::mem::size_of::<Option<usize>>()
);
assert_eq!(
core::mem::size_of::<RawReportRef<'_>>(),
core::mem::size_of::<usize>()
);
assert_eq!(
core::mem::size_of::<Option<RawReportRef<'_>>>(),
core::mem::size_of::<usize>()
);
assert_eq!(
core::mem::size_of::<Result<(), RawReportRef<'_>>>(),
core::mem::size_of::<usize>()
);
assert_eq!(
core::mem::size_of::<Result<String, RawReportRef<'_>>>(),
core::mem::size_of::<String>()
);
assert_eq!(
core::mem::size_of::<Option<Option<RawReportRef<'_>>>>(),
core::mem::size_of::<Option<usize>>()
);
assert_eq!(
core::mem::size_of::<RawReportMut<'_>>(),
core::mem::size_of::<usize>()
);
assert_eq!(
core::mem::size_of::<Option<RawReportMut<'_>>>(),
core::mem::size_of::<usize>()
);
assert_eq!(
core::mem::size_of::<Result<(), RawReportMut<'_>>>(),
core::mem::size_of::<usize>()
);
assert_eq!(
core::mem::size_of::<Result<String, RawReportMut<'_>>>(),
core::mem::size_of::<String>()
);
assert_eq!(
core::mem::size_of::<Option<Option<RawReportMut<'_>>>>(),
core::mem::size_of::<Option<usize>>()
);
}
#[test]
fn test_raw_report_get_refs() {
let report = RawReport::new::<i32, HandlerI32>(789, vec![], vec![]);
let report_ref = report.as_ref();
let ptr1 = report_ref.as_ptr();
let ptr2 = report_ref.as_ptr();
assert_eq!(ptr1, ptr2);
}
#[test]
fn test_raw_report_clone_arc() {
let report = RawReport::new::<i32, HandlerI32>(123, vec![], vec![]);
let report_ref = report.as_ref();
assert_eq!(report_ref.strong_count(), 1);
assert_eq!(report_ref.context_type_id(), TypeId::of::<i32>());
let cloned = unsafe { report_ref.clone_arc() };
let cloned_ref = cloned.as_ref();
assert_eq!(report_ref.strong_count(), 2);
assert_eq!(cloned_ref.strong_count(), 2);
assert_eq!(report_ref.context_type_id(), cloned_ref.context_type_id());
assert!(core::ptr::eq(report_ref.vtable(), cloned_ref.vtable()));
core::mem::drop(cloned);
assert_eq!(report_ref.strong_count(), 1);
}
#[test]
fn test_raw_attachment_downcast() {
let int_report = RawReport::new::<i32, HandlerI32>(42, vec![], vec![]);
let string_report =
RawReport::new::<String, HandlerString>(String::from("test"), vec![], vec![]);
let int_ref = int_report.as_ref();
let string_ref = string_report.as_ref();
assert_eq!(int_ref.context_type_id(), TypeId::of::<i32>());
assert_eq!(string_ref.context_type_id(), TypeId::of::<String>());
assert!(!core::ptr::eq(int_ref.vtable(), string_ref.vtable()));
assert_eq!(unsafe { int_ref.context_downcast_unchecked::<i32>() }, &42);
assert_eq!(
unsafe { string_ref.context_downcast_unchecked::<String>() },
"test"
);
}
#[test]
fn test_raw_report_children() {
let child = RawReport::new::<i32, HandlerI32>(1, vec![], vec![]);
let parent = RawReport::new::<i32, HandlerI32>(0, vec![child], vec![]);
let parent_ref = parent.as_ref();
assert_eq!(parent_ref.context_type_id(), TypeId::of::<i32>());
assert_eq!(
unsafe { parent_ref.context_downcast_unchecked::<i32>() },
&0
);
let children = parent_ref.children();
assert_eq!(children.len(), 1);
let child_ref = children[0].as_ref();
assert_eq!(child_ref.context_type_id(), TypeId::of::<i32>());
assert_eq!(child_ref.children().len(), 0);
assert_eq!(unsafe { child_ref.context_downcast_unchecked::<i32>() }, &1);
assert!(core::ptr::eq(parent_ref.vtable(), child_ref.vtable()));
}
#[test]
fn test_raw_report_with_attachments() {
use crate::{attachment::RawAttachment, handlers::AttachmentHandler};
struct AttachmentHandlerI32;
impl AttachmentHandler<i32> for AttachmentHandlerI32 {
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)
}
}
let attachment1 = RawAttachment::new::<i32, AttachmentHandlerI32>(100);
let attachment2 = RawAttachment::new::<i32, AttachmentHandlerI32>(200);
let child = RawReport::new::<i32, HandlerI32>(1, vec![], vec![attachment1]);
let parent = RawReport::new::<i32, HandlerI32>(0, vec![child], vec![attachment2]);
let parent_ref = parent.as_ref();
assert_eq!(parent_ref.context_type_id(), TypeId::of::<i32>());
assert_eq!(
unsafe { parent_ref.context_downcast_unchecked::<i32>() },
&0
);
let children = parent_ref.children();
let attachments = parent_ref.attachments();
assert_eq!(children.len(), 1);
assert_eq!(attachments.len(), 1);
let child_ref = children[0].as_ref();
assert_eq!(child_ref.context_type_id(), TypeId::of::<i32>());
assert_eq!(unsafe { child_ref.context_downcast_unchecked::<i32>() }, &1);
assert_eq!(child_ref.children().len(), 0);
assert_eq!(child_ref.attachments().len(), 1);
let parent_attachment_ref = attachments[0].as_ref();
let child_attachment_ref = child_ref.attachments()[0].as_ref();
assert_eq!(
parent_attachment_ref.attachment_type_id(),
TypeId::of::<i32>()
);
assert_eq!(
child_attachment_ref.attachment_type_id(),
TypeId::of::<i32>()
);
assert_eq!(
unsafe { *parent_attachment_ref.attachment_downcast_unchecked::<i32>() },
200
);
assert_eq!(
unsafe { *child_attachment_ref.attachment_downcast_unchecked::<i32>() },
100
);
assert!(core::ptr::eq(parent_ref.vtable(), child_ref.vtable()));
}
#[test]
fn test_raw_report_mut_basic() {
let mut report = RawReport::new::<i32, HandlerI32>(789, vec![], vec![]);
let mut report_mut = unsafe { report.as_mut() };
let report_ref = report_mut.as_ref();
assert_eq!(report_ref.context_type_id(), TypeId::of::<i32>());
assert_eq!(
unsafe { report_ref.context_downcast_unchecked::<i32>() },
&789
);
let reborrowed = report_mut.reborrow();
let ref_from_reborrow = reborrowed.as_ref();
assert_eq!(ref_from_reborrow.context_type_id(), TypeId::of::<i32>());
assert_eq!(
unsafe { ref_from_reborrow.context_downcast_unchecked::<i32>() },
&789
);
let ptr = report_mut.into_mut_ptr();
assert!(!ptr.is_null());
}
#[test]
fn test_raw_report_mut_reborrow_lifetime() {
let mut report =
RawReport::new::<String, HandlerString>(String::from("test"), vec![], vec![]);
let mut report_mut = unsafe { report.as_mut() };
{
let short_reborrow = report_mut.reborrow();
let ref_from_short = short_reborrow.as_ref();
assert_eq!(ref_from_short.context_type_id(), TypeId::of::<String>());
assert_eq!(
unsafe { ref_from_short.context_downcast_unchecked::<String>() },
"test"
);
}
let final_ref = report_mut.as_ref();
assert_eq!(final_ref.context_type_id(), TypeId::of::<String>());
assert_eq!(
unsafe { final_ref.context_downcast_unchecked::<String>() },
"test"
);
}
#[test]
fn test_raw_report_mut_with_children() {
let child = RawReport::new::<i32, HandlerI32>(1, vec![], vec![]);
let mut parent = RawReport::new::<i32, HandlerI32>(0, vec![child], vec![]);
let mut parent_mut = unsafe { parent.as_mut() };
let parent_ref = parent_mut.as_ref();
assert_eq!(parent_ref.context_type_id(), TypeId::of::<i32>());
assert_eq!(
unsafe { parent_ref.context_downcast_unchecked::<i32>() },
&0
);
let children = parent_ref.children();
assert_eq!(children.len(), 1);
let child_ref = children[0].as_ref();
assert_eq!(child_ref.context_type_id(), TypeId::of::<i32>());
assert_eq!(unsafe { child_ref.context_downcast_unchecked::<i32>() }, &1);
let reborrowed = parent_mut.reborrow();
let reborrow_ref = reborrowed.as_ref();
let reborrow_children = reborrow_ref.children();
assert_eq!(reborrow_children.len(), 1);
assert_eq!(
reborrow_children[0].as_ref().context_type_id(),
TypeId::of::<i32>()
);
assert_eq!(
unsafe {
reborrow_children[0]
.as_ref()
.context_downcast_unchecked::<i32>()
},
&1
);
}
#[test]
fn test_raw_report_mut_ptr_consistency() {
let mut report = RawReport::new::<i32, HandlerI32>(42, vec![], vec![]);
let immut_ref = report.as_ref();
let immut_ptr = immut_ref.as_ptr();
let report_mut = unsafe { report.as_mut() };
let mut_ptr = report_mut.into_mut_ptr();
assert_eq!(immut_ptr, mut_ptr as *const _);
}
#[test]
fn test_send_sync() {
static_assertions::assert_not_impl_any!(RawReport: Send, Sync);
static_assertions::assert_not_impl_any!(RawReportRef<'_>: Send, Sync);
static_assertions::assert_not_impl_any!(RawReportMut<'_>: Send, Sync);
}
#[test]
fn test_raw_report_context_as_any() {
let report = RawReport::new::<i32, HandlerI32>(42, vec![], vec![]);
let any = report.as_ref().context_as_any();
assert_eq!(any.downcast_ref::<i32>(), Some(&42));
assert!(any.downcast_ref::<String>().is_none());
}
#[test]
fn test_raw_report_context_as_any_mut() {
let mut report = RawReport::new::<i32, HandlerI32>(41, vec![], vec![]);
let report_mut = unsafe { report.as_mut() };
let any = report_mut.into_context_as_any_mut();
*any.downcast_mut::<i32>().unwrap() += 1;
assert_eq!(
unsafe { report.as_ref().context_downcast_unchecked::<i32>() },
&42
);
}
}