rootcause_internals/report/
raw.rs

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