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