Skip to main content

rootcause_internals/report/
raw.rs

1//! Type-erased report pointer types.
2//!
3//! This module encapsulates the `ptr` field of [`RawReport`], [`RawReportRef`],
4//! and [`RawReportMut`], ensuring it is only visible within this module. This
5//! visibility restriction guarantees the safety invariant: **the pointer always
6//! comes from `Arc<ReportData<C>>`**.
7//!
8//! # Safety Invariant
9//!
10//! Since the `ptr` field can only be set via [`RawReport::new`] or
11//! [`RawReport::from_arc`] (which create it from `Arc::into_raw`), and cannot
12//! be modified afterward (no `pub` or `pub(crate)` fields), the pointer
13//! provenance remains valid throughout the value's lifetime.
14//!
15//! The [`RawReport::drop`] implementation and reference counting operations
16//! rely on this invariant to safely reconstruct the `Arc` and manage memory.
17//!
18//! # Type Erasure
19//!
20//! The concrete type parameter `C` is erased by casting to
21//! `ReportData<Erased>`. The vtable stored within the `ReportData` provides the
22//! runtime type information needed to safely downcast and format reports.
23//!
24//! # Allocation Strategy
25//!
26//! Unlike attachments (which use `Box`), reports use `triomphe::Arc` for
27//! storage. This enables:
28//! - Cheap cloning through reference counting
29//! - Shared ownership across multiple report references
30//! - Thread-safe sharing when the context type is `Send + Sync`
31
32use alloc::vec::Vec;
33use core::{
34    any::{Any, TypeId},
35    ptr::NonNull,
36};
37
38use crate::{
39    attachment::RawAttachment,
40    handlers::{ContextFormattingStyle, ContextHandler, FormattingFunction},
41    report::data::ReportData,
42    util::Erased,
43};
44
45/// A pointer to a [`ReportData`] that is guaranteed to point to an initialized
46/// instance of a [`ReportData<C>`] for some specific `C`, though we do not know
47/// which actual `C` it is.
48///
49/// However, the pointer is allowed to transition into a non-initialized state
50/// inside the [`RawReport::drop`] method.
51///
52/// The pointer is guaranteed to have been created using
53/// [`triomphe::Arc::into_raw`].
54///
55/// We cannot use a [`triomphe::OffsetArc<ReportData<C>>`] directly, because
56/// that does not allow us to type-erase the `C`.
57#[repr(transparent)]
58pub struct RawReport {
59    /// Pointer to the inner report data
60    ///
61    /// # Safety
62    ///
63    /// The following safety invariants are guaranteed to be upheld as long as
64    /// this struct exists:
65    ///
66    /// 1. The pointer must have been created from a
67    ///    `triomphe::Arc<ReportData<C>>` for some `C` using
68    ///    `triomphe::Arc::into_raw`.
69    /// 2. The pointer retains full provenance over the `Arc` for the entire
70    ///    lifetime of this object (i.e., it was not derived from a `&T`)
71    /// 3. The pointer will point to the same `ReportData<C>` for the entire
72    ///    lifetime of this object.
73    ptr: NonNull<ReportData<Erased>>,
74}
75
76impl RawReport {
77    /// Creates a new [`RawReport`] from a [`triomphe::Arc<ReportData<C>>`].
78    #[inline]
79    pub(super) fn from_arc<C: 'static>(data: triomphe::Arc<ReportData<C>>) -> Self {
80        let ptr: *const ReportData<C> = triomphe::Arc::into_raw(data);
81        let ptr: *mut ReportData<Erased> = ptr.cast::<ReportData<Erased>>().cast_mut();
82
83        // SAFETY:
84        // 1. Triomphe guarantees that `Arc::into_raw` returns a non-null pointer.
85        let ptr: NonNull<ReportData<Erased>> = unsafe { NonNull::new_unchecked(ptr) };
86
87        Self {
88            // SAFETY:
89            // 1. We just created the pointer using `triomphe::Arc::into_raw`.
90            // 2. We have provenance and we are not locally changing that here
91            // 3. We are creating the object here and we are not changing the pointer.
92            ptr,
93        }
94    }
95
96    /// Consumes the RawReport without decrementing the reference count and
97    /// returns the inner pointer.
98    #[inline]
99    pub(super) fn into_non_null(self) -> NonNull<ReportData<Erased>> {
100        let ptr = self.ptr;
101        core::mem::forget(self);
102        ptr
103    }
104
105    /// Creates a new [`RawReport`] with the specified handler, context,
106    /// children, and attachments.
107    ///
108    /// The created report will have the supplied context type and handler type.
109    /// It will also have a strong count of 1.
110    #[inline]
111    pub fn new<C, H>(context: C, children: Vec<RawReport>, attachments: Vec<RawAttachment>) -> Self
112    where
113        C: 'static,
114        H: ContextHandler<C>,
115    {
116        let data = triomphe::Arc::new(ReportData::new::<H>(context, children, attachments));
117        Self::from_arc(data)
118    }
119
120    /// Returns a reference to the [`ReportData`] instance.
121    #[inline]
122    pub fn as_ref(&self) -> RawReportRef<'_> {
123        RawReportRef {
124            // SAFETY:
125            // 1. Guaranteed by the invariants on `RawReport`
126            // 2. Guaranteed by the invariants on `RawReportMut` and the fact that we are taking a
127            //    shared reference to `self`
128            // 3. We are creating the `RawReportRef` here, and we are not changing the pointer
129            ptr: self.ptr,
130            _marker: core::marker::PhantomData,
131        }
132    }
133
134    /// Returns a mutable reference to the [`ReportData`] instance.
135    ///
136    /// # Safety
137    ///
138    /// The caller must ensure:
139    ///
140    /// 1. This is the only existing reference pointing to the inner
141    ///    [`ReportData`]. Specifically the strong count of the inner
142    ///    [`triomphe::Arc`] must be `1`.
143    #[inline]
144    pub unsafe fn as_mut(&mut self) -> RawReportMut<'_> {
145        RawReportMut {
146            // SAFETY:
147            // 1. The pointer comes from `Arc::into_raw` (guaranteed by `RawReport`'s invariant)
148            // 2. We are creating the `RawReportMut` here, and we are not changing the pointer
149            // 3. Exclusive mutable access is guaranteed by the caller's obligation that no other
150            //    references to the inner `ReportData` exist
151            ptr: self.ptr,
152            _marker: core::marker::PhantomData,
153        }
154    }
155}
156
157impl core::ops::Drop for RawReport {
158    #[inline]
159    fn drop(&mut self) {
160        let vtable = self.as_ref().vtable();
161
162        // SAFETY:
163        // 1. The pointer comes from `Arc::into_raw` (guaranteed by `RawReport::new`)
164        // 2. The vtable returned by `self.as_ref().vtable()` is guaranteed to match the
165        //    data in the `ReportData`.
166        // 3. The pointer is not used after this call (we're in the drop function)
167        unsafe {
168            vtable.drop(self.ptr);
169        }
170    }
171}
172
173/// A lifetime-bound pointer to a [`ReportData`] that is guaranteed to point
174/// to an initialized instance of a [`ReportData<C>`] for some specific `C`,
175/// though we do not know which actual `C` it is.
176///
177/// We cannot use a [`&'a ReportData<C>`] directly, because that would require
178/// us to know the actual type of the context, which we do not.
179///
180/// [`&'a ReportData<C>`]: ReportData
181///
182/// # Safety invariants
183///
184/// This reference behaves like a `&'a ReportData<C>` for some unknown
185/// `C` and upholds the usual safety invariants of shared references:
186///
187/// 1. The pointee is properly initialized for the entire lifetime `'a`.
188/// 2. The pointee is not mutated for the entire lifetime `'a`.
189#[derive(Clone, Copy)]
190#[repr(transparent)]
191pub struct RawReportRef<'a> {
192    /// Pointer to the inner report data
193    ///
194    /// # Safety
195    ///
196    /// The following safety invariants are guaranteed to be upheld as long as
197    /// this struct exists:
198    ///
199    /// 1. The pointer must have been created from a
200    ///    `triomphe::Arc<ReportData<C>>` for some `C` using
201    ///    `triomphe::Arc::into_raw`.
202    /// 2. The pointer retains full provenance over the `Arc` for the entire
203    ///    lifetime of this object (i.e., it was not derived from a `&T`)
204    /// 3. The pointer will point to the same `ReportData<C>` for the entire
205    ///    lifetime of this object.
206    ptr: NonNull<ReportData<Erased>>,
207
208    /// Marker to tell the compiler that we should
209    /// behave the same as a `&'a ReportData<Erased>`
210    _marker: core::marker::PhantomData<&'a ReportData<Erased>>,
211}
212
213impl<'a> RawReportRef<'a> {
214    /// Casts the [`RawReportRef`] to a [`ReportData<C>`] reference.
215    ///
216    /// # Safety
217    ///
218    /// The caller must ensure:
219    ///
220    /// 1. The type `C` matches the actual context type stored in the
221    ///    [`ReportData`]
222    #[inline]
223    pub(super) unsafe fn cast_inner<C>(self) -> &'a ReportData<C> {
224        // Debug assertion to catch type mismatches in case of bugs
225        debug_assert_eq!(self.vtable().type_id(), TypeId::of::<C>());
226
227        let this = self.ptr.cast::<ReportData<C>>();
228        // SAFETY: Converting the NonNull pointer to a reference is sound because:
229        // - The pointer is non-null, properly aligned, and dereferenceable (guaranteed
230        //   by RawReportRef's type invariants)
231        // - The pointee is properly initialized (RawReportRef's doc comment guarantees
232        //   it points to an initialized ReportData<C> for some C)
233        // - The type `C` matches the actual context type (guaranteed by caller)
234        // - Shared access is allowed
235        // - The reference lifetime 'a is valid (tied to RawReportRef<'a>'s lifetime)
236        unsafe { this.as_ref() }
237    }
238
239    /// Returns a [`NonNull`] pointer to the [`ReportData`] instance.
240    #[inline]
241    pub(super) fn as_ptr(self) -> *const ReportData<Erased> {
242        self.ptr.as_ptr()
243    }
244
245    /// Returns the [`TypeId`] of the context.
246    #[inline]
247    pub fn context_type_id(self) -> TypeId {
248        self.vtable().type_id()
249    }
250
251    /// Returns the [`core::any::type_name`] of the context.
252    #[inline]
253    pub fn context_type_name(self) -> &'static str {
254        self.vtable().type_name()
255    }
256
257    /// Returns the [`TypeId`] of the context.
258    #[inline]
259    pub fn context_handler_type_id(self) -> TypeId {
260        self.vtable().handler_type_id()
261    }
262
263    /// Returns the source of the context using the [`ContextHandler::source`]
264    /// method specified when the [`ReportData`] was created.
265    #[inline]
266    pub fn context_source(self) -> Option<&'a (dyn core::error::Error + 'static)> {
267        let vtable = self.vtable();
268        // SAFETY:
269        // 1. The vtable returned by `self.vtable()` is guaranteed to match the data in
270        //    the `ReportData`.
271        unsafe { vtable.source(self) }
272    }
273
274    /// Formats the context by using the [`ContextHandler::display`] method
275    /// specified by the handler used to create the [`ReportData`].
276    #[inline]
277    pub fn context_display(self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
278        let vtable = self.vtable();
279        // SAFETY:
280        // 1. The vtable returned by `self.vtable()` is guaranteed to match the data in
281        //    the `ReportData`.
282        unsafe { vtable.display(self, formatter) }
283    }
284
285    /// Formats the context by using the [`ContextHandler::debug`] method
286    /// specified by the handler used to create the [`ReportData`].
287    #[inline]
288    pub fn context_debug(self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
289        let vtable = self.vtable();
290        // SAFETY:
291        // 1. The vtable returned by `self.vtable()` is guaranteed to match the data in
292        //    the `ReportData`.
293        unsafe { vtable.debug(self, formatter) }
294    }
295
296    /// The formatting style preferred by the context when formatted as part of
297    /// a report.
298    ///
299    /// # Arguments
300    ///
301    /// - `report_formatting_function`: Whether the report in which this context
302    ///   will be embedded is being formatted using [`Display`] formatting or
303    ///   [`Debug`]
304    ///
305    /// [`Display`]: core::fmt::Display
306    /// [`Debug`]: core::fmt::Debug
307    #[inline]
308    pub fn preferred_context_formatting_style(
309        self,
310        report_formatting_function: FormattingFunction,
311    ) -> ContextFormattingStyle {
312        let vtable = self.vtable();
313        // SAFETY:
314        // 1. The vtable returned by `self.vtable()` is guaranteed to match the data in
315        //    the `ReportData`.
316        unsafe {
317            // @add-unsafe-context: ReportData
318            vtable.preferred_context_formatting_style(self, report_formatting_function)
319        }
320    }
321
322    /// Clones the inner [`triomphe::Arc`] and returns a new [`RawReport`]
323    /// pointing to the same data.
324    ///
325    /// # Safety
326    ///
327    /// The caller must ensure:
328    ///
329    /// 1. All other references to this report are compatible with shared
330    ///    ownership. Specifically none of them assume that the strong_count is
331    ///    `1`.
332    #[inline]
333    pub unsafe fn clone_arc(self) -> RawReport {
334        let vtable = self.vtable();
335        // SAFETY:
336        // 1. Guaranteed by invariants on this type
337        // 2. Guaranteed by invariants on this type
338        // 3. The vtable returned by `self.vtable()` is guaranteed to match the data in
339        //    the `ReportData`.
340        // 4. Guaranteed by the caller
341        unsafe {
342            // @add-unsafe-context: ReportData
343            vtable.clone_arc(self.ptr)
344        }
345    }
346
347    /// Returns a [`&dyn Any`](Any) view of the context.
348    ///
349    /// The returned reference can be downcast using
350    /// `<dyn Any>::downcast_ref`.
351    #[inline]
352    pub fn context_as_any(self) -> &'a (dyn Any + 'static) {
353        let vtable = self.vtable();
354        // SAFETY:
355        // 1. The vtable returned by `self.vtable()` is guaranteed to match the data in
356        //    the `ReportData`.
357        unsafe {
358            // @add-unsafe-context: ReportData
359            vtable.context_as_any(self)
360        }
361    }
362
363    /// Gets the strong_count of the inner [`triomphe::Arc`].
364    #[inline]
365    pub fn strong_count(self) -> usize {
366        let vtable = self.vtable();
367        // SAFETY:
368        // 1. The vtable returned by `self.vtable()` is guaranteed to match the data in
369        //    the `ReportData`.
370        unsafe {
371            // @add-unsafe-context: ReportData
372            vtable.strong_count(self)
373        }
374    }
375}
376
377/// A mutable lifetime-bound pointer to a [`ReportData`] that is guaranteed to
378/// point to an initialized instance of a [`ReportData<C>`] for some specific
379/// `C`, though we do not know which actual `C` it is.
380///
381/// We cannot use a [`&'a mut ReportData<C>`] directly, because that would
382/// require us to know the actual type of the context, which we do not.
383///
384/// [`&'a mut ReportData<C>`]: ReportData
385///
386/// # Safety invariants
387///
388/// This reference behaves like a `&'a mut ReportData<C>` for some unknown
389/// `C` and upholds the usual safety invariants of mutable references:
390///
391/// 1. The pointee is properly initialized for the entire lifetime `'a`.
392/// 2. The pointee is not aliased for the entire lifetime `'a`.
393/// 3. Like a `&'a mut T`, it is possible to reborrow this reference to a
394///    shorter lifetime. The borrow checker will ensure that original longer
395///    lifetime is not used while the shorter lifetime exists.
396#[repr(transparent)]
397pub struct RawReportMut<'a> {
398    /// Pointer to the inner report data
399    ///
400    /// # Safety
401    ///
402    /// The following safety invariants are guaranteed to be upheld as long as
403    /// this struct exists:
404    ///
405    /// 1. The pointer must have been created from a
406    ///    `triomphe::Arc<ReportData<C>>` for some `C` using
407    ///    `triomphe::Arc::into_raw`.
408    /// 2. The pointer will point to the same `ReportData<C>` for the entire
409    ///    lifetime of this object.
410    /// 3. This pointer is valid for exclusive mutable access to the
411    ///    `ReportData` with the same semantics as a `&'a mut ReportData<C>`.
412    ptr: NonNull<ReportData<Erased>>,
413
414    /// Marker to tell the compiler that we should
415    /// behave the same as a `&'a mut ReportData<Erased>`
416    _marker: core::marker::PhantomData<&'a mut ReportData<Erased>>,
417}
418
419impl<'a> RawReportMut<'a> {
420    /// Casts the [`RawReportMut`] to a mutable [`ReportData<C>`] reference.
421    ///
422    /// # Safety
423    ///
424    /// The caller must ensure:
425    ///
426    /// 1. The type `C` matches the actual context type stored in the
427    ///    [`ReportData`]
428    #[inline]
429    pub(super) unsafe fn cast_inner<C>(self) -> &'a mut ReportData<C> {
430        // Debug assertion to catch type mismatches in case of bugs
431        debug_assert_eq!(self.as_ref().vtable().type_id(), TypeId::of::<C>());
432
433        let mut this = self.ptr.cast::<ReportData<C>>();
434
435        // SAFETY: Converting the NonNull pointer to a mutable reference is sound
436        // because:
437        // - The pointer is non-null, properly aligned, and dereferenceable (guaranteed
438        //   by RawReportMut's type invariants)
439        // - The pointee is properly initialized (RawReportMut's doc comment guarantees
440        //   it points to an initialized ReportData<C> for some C)
441        // - The type `C` matches the actual context type (guaranteed by caller)
442        // - Exclusive access is guaranteed
443        // - The reference lifetime 'a is valid (tied to RawReportMut<'a>'s lifetime)
444        unsafe { this.as_mut() }
445    }
446
447    /// Reborrows the mutable reference to the [`ReportData`] with a shorter
448    /// lifetime.
449    #[inline]
450    pub fn reborrow<'b>(&'b mut self) -> RawReportMut<'b> {
451        RawReportMut {
452            // SAFETY:
453            // 1. Guaranteed by invariant on `self`
454            // 2. We are creating the `RawReportMut` here, and we are not changing the pointer
455            // 3. Upheld by mutable borrow of `self`
456            ptr: self.ptr,
457            _marker: core::marker::PhantomData,
458        }
459    }
460
461    /// Returns a reference to the [`ReportData`] instance.
462    #[inline]
463    pub fn as_ref(&self) -> RawReportRef<'_> {
464        RawReportRef {
465            // SAFETY:
466            // 1. Guaranteed by the invariants on `RawReportMut`
467            // 2. Guaranteed by the invariants on `RawReportMut` and the fact that we are taking a
468            //    shared reference to `self`
469            // 3. We are creating the `RawReportRef` here, and we are not changing the pointer
470            ptr: self.ptr,
471            _marker: core::marker::PhantomData,
472        }
473    }
474
475    /// Consumes the mutable reference and returns an immutable one with the
476    /// same lifetime.
477    #[inline]
478    pub fn into_ref(self) -> RawReportRef<'a> {
479        RawReportRef {
480            // SAFETY:
481            // 1. Guaranteed by the invariants on `RawReportMut`
482            // 2. Guaranteed by the invariants on `RawReportMut` and the fact that we are consuming
483            //    `self`
484            // 3. We are creating the `RawReportRef` here, and we are not changing the pointer
485            ptr: self.ptr,
486            _marker: core::marker::PhantomData,
487        }
488    }
489
490    /// Consumes this [`RawReportMut`] and returns a raw mutable pointer to the
491    /// underlying [`ReportData`].
492    ///
493    /// This method is primarily used for internal operations that require
494    /// direct pointer access.
495    #[inline]
496    pub(super) fn into_mut_ptr(self) -> *mut ReportData<Erased> {
497        self.ptr.as_ptr()
498    }
499
500    /// Consumes this [`RawReportMut`] and returns a [`&mut dyn Any`](Any) view
501    /// of the context with the same lifetime.
502    ///
503    /// The returned reference can be downcast using
504    /// `<dyn Any>::downcast_mut`.
505    #[inline]
506    pub fn into_context_as_any_mut(self) -> &'a mut (dyn Any + 'static) {
507        let vtable = self.as_ref().vtable();
508        // SAFETY:
509        // 1. The vtable returned by `self.as_ref().vtable()` is guaranteed to match the
510        //    data in the `ReportData`.
511        unsafe {
512            // @add-unsafe-context: ReportData
513            vtable.context_as_any_mut(self)
514        }
515    }
516}
517
518#[cfg(test)]
519mod tests {
520    use alloc::{string::String, vec};
521    use core::{error::Error, fmt};
522
523    use super::*;
524    use crate::handlers::ContextHandler;
525
526    struct HandlerI32;
527    impl ContextHandler<i32> for HandlerI32 {
528        fn source(_value: &i32) -> Option<&(dyn Error + 'static)> {
529            None
530        }
531
532        fn display(value: &i32, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
533            fmt::Display::fmt(value, formatter)
534        }
535
536        fn debug(value: &i32, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
537            fmt::Debug::fmt(value, formatter)
538        }
539    }
540
541    struct HandlerString;
542    impl ContextHandler<String> for HandlerString {
543        fn source(_value: &String) -> Option<&(dyn Error + 'static)> {
544            None
545        }
546
547        fn display(value: &String, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
548            fmt::Display::fmt(value, formatter)
549        }
550
551        fn debug(value: &String, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
552            fmt::Debug::fmt(value, formatter)
553        }
554    }
555
556    #[test]
557    fn test_raw_report_size() {
558        assert_eq!(
559            core::mem::size_of::<RawReport>(),
560            core::mem::size_of::<usize>()
561        );
562        assert_eq!(
563            core::mem::size_of::<Option<RawReport>>(),
564            core::mem::size_of::<usize>()
565        );
566        assert_eq!(
567            core::mem::size_of::<Result<(), RawReport>>(),
568            core::mem::size_of::<usize>()
569        );
570        assert_eq!(
571            core::mem::size_of::<Result<String, RawReport>>(),
572            core::mem::size_of::<String>()
573        );
574        assert_eq!(
575            core::mem::size_of::<Option<Option<RawReport>>>(),
576            core::mem::size_of::<Option<usize>>()
577        );
578
579        assert_eq!(
580            core::mem::size_of::<RawReportRef<'_>>(),
581            core::mem::size_of::<usize>()
582        );
583        assert_eq!(
584            core::mem::size_of::<Option<RawReportRef<'_>>>(),
585            core::mem::size_of::<usize>()
586        );
587        assert_eq!(
588            core::mem::size_of::<Result<(), RawReportRef<'_>>>(),
589            core::mem::size_of::<usize>()
590        );
591        assert_eq!(
592            core::mem::size_of::<Result<String, RawReportRef<'_>>>(),
593            core::mem::size_of::<String>()
594        );
595        assert_eq!(
596            core::mem::size_of::<Option<Option<RawReportRef<'_>>>>(),
597            core::mem::size_of::<Option<usize>>()
598        );
599
600        assert_eq!(
601            core::mem::size_of::<RawReportMut<'_>>(),
602            core::mem::size_of::<usize>()
603        );
604        assert_eq!(
605            core::mem::size_of::<Option<RawReportMut<'_>>>(),
606            core::mem::size_of::<usize>()
607        );
608        assert_eq!(
609            core::mem::size_of::<Result<(), RawReportMut<'_>>>(),
610            core::mem::size_of::<usize>()
611        );
612        assert_eq!(
613            core::mem::size_of::<Result<String, RawReportMut<'_>>>(),
614            core::mem::size_of::<String>()
615        );
616        assert_eq!(
617            core::mem::size_of::<Option<Option<RawReportMut<'_>>>>(),
618            core::mem::size_of::<Option<usize>>()
619        );
620    }
621
622    #[test]
623    fn test_raw_report_get_refs() {
624        let report = RawReport::new::<i32, HandlerI32>(789, vec![], vec![]);
625        let report_ref = report.as_ref();
626
627        // Accessing the pointer multiple times should be safe and consistent
628        let ptr1 = report_ref.as_ptr();
629        let ptr2 = report_ref.as_ptr();
630        assert_eq!(ptr1, ptr2);
631    }
632
633    #[test]
634    fn test_raw_report_clone_arc() {
635        // Test that Arc cloning maintains safety
636        let report = RawReport::new::<i32, HandlerI32>(123, vec![], vec![]);
637        let report_ref = report.as_ref();
638
639        assert_eq!(report_ref.strong_count(), 1);
640
641        // Original should have valid data
642        assert_eq!(report_ref.context_type_id(), TypeId::of::<i32>());
643
644        // Clone should work and maintain same type
645        // SAFETY: There are no assumptions on single ownership
646        let cloned = unsafe { report_ref.clone_arc() };
647        let cloned_ref = cloned.as_ref();
648
649        assert_eq!(report_ref.strong_count(), 2);
650        assert_eq!(cloned_ref.strong_count(), 2);
651
652        // Both should have same type and vtable
653        assert_eq!(report_ref.context_type_id(), cloned_ref.context_type_id());
654        assert!(core::ptr::eq(report_ref.vtable(), cloned_ref.vtable()));
655
656        core::mem::drop(cloned);
657
658        // After dropping the strong count should go back down
659        assert_eq!(report_ref.strong_count(), 1);
660    }
661
662    #[test]
663    fn test_raw_attachment_downcast() {
664        let int_report = RawReport::new::<i32, HandlerI32>(42, vec![], vec![]);
665        let string_report =
666            RawReport::new::<String, HandlerString>(String::from("test"), vec![], vec![]);
667
668        let int_ref = int_report.as_ref();
669        let string_ref = string_report.as_ref();
670
671        // Are TypeIds what we expect?
672        assert_eq!(int_ref.context_type_id(), TypeId::of::<i32>());
673        assert_eq!(string_ref.context_type_id(), TypeId::of::<String>());
674
675        // The vtables should be different
676        assert!(!core::ptr::eq(int_ref.vtable(), string_ref.vtable()));
677
678        // Correct downcasting should work
679        assert_eq!(unsafe { int_ref.context_downcast_unchecked::<i32>() }, &42);
680        assert_eq!(
681            unsafe { string_ref.context_downcast_unchecked::<String>() },
682            "test"
683        );
684    }
685
686    #[test]
687    fn test_raw_report_children() {
688        let child = RawReport::new::<i32, HandlerI32>(1, vec![], vec![]);
689        let parent = RawReport::new::<i32, HandlerI32>(0, vec![child], vec![]);
690
691        let parent_ref = parent.as_ref();
692        assert_eq!(parent_ref.context_type_id(), TypeId::of::<i32>());
693        assert_eq!(
694            unsafe { parent_ref.context_downcast_unchecked::<i32>() },
695            &0
696        );
697
698        // Parent should have one child
699        let children = parent_ref.children();
700        assert_eq!(children.len(), 1);
701
702        // Child should be accessible safely
703        let child_ref = children[0].as_ref();
704        assert_eq!(child_ref.context_type_id(), TypeId::of::<i32>());
705        assert_eq!(child_ref.children().len(), 0);
706        assert_eq!(unsafe { child_ref.context_downcast_unchecked::<i32>() }, &1);
707
708        // Both should have same vtable (same type)
709        assert!(core::ptr::eq(parent_ref.vtable(), child_ref.vtable()));
710    }
711
712    #[test]
713    fn test_raw_report_with_attachments() {
714        use crate::{attachment::RawAttachment, handlers::AttachmentHandler};
715
716        // Create a simple attachment handler for i32
717        struct AttachmentHandlerI32;
718        impl AttachmentHandler<i32> for AttachmentHandlerI32 {
719            fn display(value: &i32, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
720                fmt::Display::fmt(value, formatter)
721            }
722
723            fn debug(value: &i32, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
724                fmt::Debug::fmt(value, formatter)
725            }
726        }
727
728        // Create some attachments
729        let attachment1 = RawAttachment::new::<i32, AttachmentHandlerI32>(100);
730        let attachment2 = RawAttachment::new::<i32, AttachmentHandlerI32>(200);
731
732        // Create a child report with one attachment
733        let child = RawReport::new::<i32, HandlerI32>(1, vec![], vec![attachment1]);
734
735        // Create a parent report with the child and another attachment
736        let parent = RawReport::new::<i32, HandlerI32>(0, vec![child], vec![attachment2]);
737
738        let parent_ref = parent.as_ref();
739        assert_eq!(parent_ref.context_type_id(), TypeId::of::<i32>());
740        assert_eq!(
741            unsafe { parent_ref.context_downcast_unchecked::<i32>() },
742            &0
743        );
744
745        // Parent should have one child and one attachment
746        let children = parent_ref.children();
747        let attachments = parent_ref.attachments();
748        assert_eq!(children.len(), 1);
749        assert_eq!(attachments.len(), 1);
750
751        // Child should be accessible safely and have one attachment
752        let child_ref = children[0].as_ref();
753        assert_eq!(child_ref.context_type_id(), TypeId::of::<i32>());
754        assert_eq!(unsafe { child_ref.context_downcast_unchecked::<i32>() }, &1);
755        assert_eq!(child_ref.children().len(), 0);
756        assert_eq!(child_ref.attachments().len(), 1);
757
758        // Check attachment downcasting works
759        let parent_attachment_ref = attachments[0].as_ref();
760        let child_attachment_ref = child_ref.attachments()[0].as_ref();
761
762        assert_eq!(
763            parent_attachment_ref.attachment_type_id(),
764            TypeId::of::<i32>()
765        );
766        assert_eq!(
767            child_attachment_ref.attachment_type_id(),
768            TypeId::of::<i32>()
769        );
770
771        // Downcast attachments and verify values
772        assert_eq!(
773            unsafe { *parent_attachment_ref.attachment_downcast_unchecked::<i32>() },
774            200
775        );
776        assert_eq!(
777            unsafe { *child_attachment_ref.attachment_downcast_unchecked::<i32>() },
778            100
779        );
780
781        // Both reports should have same vtable (same context type)
782        assert!(core::ptr::eq(parent_ref.vtable(), child_ref.vtable()));
783    }
784
785    #[test]
786    fn test_raw_report_mut_basic() {
787        let mut report = RawReport::new::<i32, HandlerI32>(789, vec![], vec![]);
788
789        // SAFETY: We have unique ownership of the report
790        let mut report_mut = unsafe { report.as_mut() };
791
792        // Test that we can get a reference from the mutable reference
793        let report_ref = report_mut.as_ref();
794        assert_eq!(report_ref.context_type_id(), TypeId::of::<i32>());
795        assert_eq!(
796            unsafe { report_ref.context_downcast_unchecked::<i32>() },
797            &789
798        );
799
800        // Test reborrow functionality
801        let reborrowed = report_mut.reborrow();
802        let ref_from_reborrow = reborrowed.as_ref();
803        assert_eq!(ref_from_reborrow.context_type_id(), TypeId::of::<i32>());
804        assert_eq!(
805            unsafe { ref_from_reborrow.context_downcast_unchecked::<i32>() },
806            &789
807        );
808
809        // Test into_mut_ptr
810        let ptr = report_mut.into_mut_ptr();
811        assert!(!ptr.is_null());
812    }
813
814    #[test]
815    fn test_raw_report_mut_reborrow_lifetime() {
816        let mut report =
817            RawReport::new::<String, HandlerString>(String::from("test"), vec![], vec![]);
818
819        // SAFETY: We have unique ownership of the report
820        let mut report_mut = unsafe { report.as_mut() };
821
822        // Test that reborrow works with different lifetimes
823        {
824            let short_reborrow = report_mut.reborrow();
825            let ref_from_short = short_reborrow.as_ref();
826            assert_eq!(ref_from_short.context_type_id(), TypeId::of::<String>());
827            assert_eq!(
828                unsafe { ref_from_short.context_downcast_unchecked::<String>() },
829                "test"
830            );
831        }
832
833        // Original mutable reference should still be usable
834        let final_ref = report_mut.as_ref();
835        assert_eq!(final_ref.context_type_id(), TypeId::of::<String>());
836        assert_eq!(
837            unsafe { final_ref.context_downcast_unchecked::<String>() },
838            "test"
839        );
840    }
841
842    #[test]
843    fn test_raw_report_mut_with_children() {
844        let child = RawReport::new::<i32, HandlerI32>(1, vec![], vec![]);
845        let mut parent = RawReport::new::<i32, HandlerI32>(0, vec![child], vec![]);
846
847        // SAFETY: We have unique ownership of the parent report
848        let mut parent_mut = unsafe { parent.as_mut() };
849
850        let parent_ref = parent_mut.as_ref();
851        assert_eq!(parent_ref.context_type_id(), TypeId::of::<i32>());
852        assert_eq!(
853            unsafe { parent_ref.context_downcast_unchecked::<i32>() },
854            &0
855        );
856
857        // Check that children are still accessible through the reference
858        let children = parent_ref.children();
859        assert_eq!(children.len(), 1);
860
861        let child_ref = children[0].as_ref();
862        assert_eq!(child_ref.context_type_id(), TypeId::of::<i32>());
863        assert_eq!(unsafe { child_ref.context_downcast_unchecked::<i32>() }, &1);
864
865        // Test reborrow with children
866        let reborrowed = parent_mut.reborrow();
867        let reborrow_ref = reborrowed.as_ref();
868        let reborrow_children = reborrow_ref.children();
869        assert_eq!(reborrow_children.len(), 1);
870        assert_eq!(
871            reborrow_children[0].as_ref().context_type_id(),
872            TypeId::of::<i32>()
873        );
874        assert_eq!(
875            unsafe {
876                reborrow_children[0]
877                    .as_ref()
878                    .context_downcast_unchecked::<i32>()
879            },
880            &1
881        );
882    }
883
884    #[test]
885    fn test_raw_report_mut_ptr_consistency() {
886        let mut report = RawReport::new::<i32, HandlerI32>(42, vec![], vec![]);
887
888        // Get immutable reference pointer first
889        let immut_ref = report.as_ref();
890        let immut_ptr = immut_ref.as_ptr();
891        // SAFETY: We have unique ownership of the report
892        let report_mut = unsafe { report.as_mut() };
893
894        // Get mutable pointer
895        let mut_ptr = report_mut.into_mut_ptr();
896
897        // Both pointers should point to the same location
898        assert_eq!(immut_ptr, mut_ptr as *const _);
899    }
900    #[test]
901    fn test_send_sync() {
902        static_assertions::assert_not_impl_any!(RawReport: Send, Sync);
903        static_assertions::assert_not_impl_any!(RawReportRef<'_>: Send, Sync);
904        static_assertions::assert_not_impl_any!(RawReportMut<'_>: Send, Sync);
905    }
906
907    #[test]
908    fn test_raw_report_context_as_any() {
909        let report = RawReport::new::<i32, HandlerI32>(42, vec![], vec![]);
910        let any = report.as_ref().context_as_any();
911        assert_eq!(any.downcast_ref::<i32>(), Some(&42));
912        assert!(any.downcast_ref::<String>().is_none());
913    }
914
915    #[test]
916    fn test_raw_report_context_as_any_mut() {
917        let mut report = RawReport::new::<i32, HandlerI32>(41, vec![], vec![]);
918
919        // SAFETY: We have unique ownership of the report
920        let report_mut = unsafe { report.as_mut() };
921        let any = report_mut.into_context_as_any_mut();
922        *any.downcast_mut::<i32>().unwrap() += 1;
923
924        assert_eq!(
925            unsafe { report.as_ref().context_downcast_unchecked::<i32>() },
926            &42
927        );
928    }
929}