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