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