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