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<'a>(&'a self) -> RawReportRef<'a> {
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<'a>(&'a mut self) -> RawReportMut<'a> {
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    /// - `report_formatting_alternate`: Whether the report in which this attachment will be embedded is being formatted using the [`alternate`] mode
200    ///
201    /// [`Display`]: core::fmt::Display
202    /// [`Debug`]: core::fmt::Debug
203    /// [`alternate`]: core::fmt::Formatter::alternate
204    pub fn preferred_context_formatting_style(
205        self,
206        report_formatting_function: FormattingFunction,
207        report_formatting_alternate: bool,
208    ) -> ContextFormattingStyle {
209        let vtable = self.vtable();
210        // SAFETY: We must ensure that the `C` of the `ReportData` matches the `C` of the `ReportVtable`.
211        // However, the only way to construct a `ReportData` is through the `ReportData::new` method,
212        // which ensures this fact.
213        unsafe {
214            vtable.preferred_context_formatting_style(
215                self,
216                report_formatting_function,
217                report_formatting_alternate,
218            )
219        }
220    }
221
222    /// Clones the inner [`triomphe::Arc`] and returns a new [`RawReport`] pointing to the same data.
223    ///
224    /// # Safety
225    ///
226    /// There must be no external assumptions that there is unique ownership of the [`triomphe::Arc`].
227    pub unsafe fn clone_arc(self) -> RawReport {
228        let vtable = self.vtable();
229        // SAFETY: We must ensure that the `C` of the `ReportData` matches the `C` of the `ReportVtable`.
230        // However, the only way to construct a `ReportData` is through the `ReportData::new` method,
231        // which ensures this fact.
232        //
233        // We must also ensure that there are no external assumptions that
234        // there is unique ownership of the `Arc`. However, this is guaranteed by
235        // our caller.
236        unsafe { vtable.clone_arc(self.ptr) }
237    }
238
239    /// Gets the strong_count of the inner [`triomphe::Arc`].
240    pub fn strong_count(self) -> usize {
241        let vtable = self.vtable();
242        // SAFETY: We must ensure that the `C` of the `ReportData` matches the `C` of the `ReportVtable`.
243        // However, the only way to construct a `ReportData` is through the `ReportData::new` method,
244        // which ensures this fact.
245        unsafe { vtable.strong_count(self.ptr) }
246    }
247}
248
249/// A mutable lifetime-bound pointer to a [`ReportData`] that is guaranteed to point
250/// to an initialized instance of a [`ReportData<C>`] for some specific `C`, though
251/// we do not know which actual `C` it is.
252///
253/// We cannot use a [`&'a mut ReportData<C>`] directly, because that would require
254/// us to know the actual type of the context, which we do not.
255///
256/// [`&'a mut ReportData<C>`]: ReportData
257#[repr(transparent)]
258pub struct RawReportMut<'a> {
259    ptr: NonNull<ReportData<Erased>>,
260    _marker: core::marker::PhantomData<&'a mut ReportData<Erased>>,
261}
262
263impl<'a> RawReportMut<'a> {
264    /// Reborrows the mutable reference to the [`ReportData`] with a shorter lifetime.
265    pub fn reborrow<'b>(&'b mut self) -> RawReportMut<'b> {
266        RawReportMut {
267            ptr: self.ptr,
268            _marker: core::marker::PhantomData,
269        }
270    }
271
272    /// Returns a reference to the [`ReportData`] instance.
273    pub fn as_ref(&self) -> RawReportRef<'_> {
274        RawReportRef {
275            ptr: self.ptr,
276            _marker: core::marker::PhantomData,
277        }
278    }
279
280    /// Consumes the mutable reference and returns an immutable one with the same lifetime.
281    pub fn into_ref(self) -> RawReportRef<'a> {
282        RawReportRef {
283            ptr: self.ptr,
284            _marker: core::marker::PhantomData,
285        }
286    }
287
288    /// Consumes this [`RawReportMut`] and returns a raw mutable pointer to the underlying [`ReportData`].
289    ///
290    /// This method is primarily used for internal operations that require direct pointer access.
291    pub(super) fn into_mut_ptr(self) -> *mut ReportData<Erased> {
292        self.ptr.as_ptr()
293    }
294}
295
296#[cfg(test)]
297mod tests {
298    use alloc::{string::String, vec};
299    use core::{error::Error, fmt};
300
301    use super::*;
302    use crate::handlers::ContextHandler;
303
304    struct HandlerI32;
305    impl ContextHandler<i32> for HandlerI32 {
306        fn source(_value: &i32) -> Option<&(dyn Error + 'static)> {
307            None
308        }
309        fn display(value: &i32, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
310            fmt::Display::fmt(value, formatter)
311        }
312        fn debug(value: &i32, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
313            fmt::Debug::fmt(value, formatter)
314        }
315    }
316
317    struct HandlerString;
318    impl ContextHandler<String> for HandlerString {
319        fn source(_value: &String) -> Option<&(dyn Error + 'static)> {
320            None
321        }
322        fn display(value: &String, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
323            fmt::Display::fmt(value, formatter)
324        }
325        fn debug(value: &String, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
326            fmt::Debug::fmt(value, formatter)
327        }
328    }
329
330    #[test]
331    fn test_raw_report_size() {
332        assert_eq!(
333            core::mem::size_of::<RawReport>(),
334            core::mem::size_of::<usize>()
335        );
336        assert_eq!(
337            core::mem::size_of::<Option<RawReport>>(),
338            core::mem::size_of::<usize>()
339        );
340        assert_eq!(
341            core::mem::size_of::<Result<(), RawReport>>(),
342            core::mem::size_of::<usize>()
343        );
344        assert_eq!(
345            core::mem::size_of::<Result<String, RawReport>>(),
346            core::mem::size_of::<String>()
347        );
348        assert_eq!(
349            core::mem::size_of::<Option<Option<RawReport>>>(),
350            core::mem::size_of::<Option<usize>>()
351        );
352
353        assert_eq!(
354            core::mem::size_of::<RawReportRef<'_>>(),
355            core::mem::size_of::<usize>()
356        );
357        assert_eq!(
358            core::mem::size_of::<Option<RawReportRef<'_>>>(),
359            core::mem::size_of::<usize>()
360        );
361        assert_eq!(
362            core::mem::size_of::<Result<(), RawReportRef<'_>>>(),
363            core::mem::size_of::<usize>()
364        );
365        assert_eq!(
366            core::mem::size_of::<Result<String, RawReportRef<'_>>>(),
367            core::mem::size_of::<String>()
368        );
369        assert_eq!(
370            core::mem::size_of::<Option<Option<RawReportRef<'_>>>>(),
371            core::mem::size_of::<Option<usize>>()
372        );
373
374        assert_eq!(
375            core::mem::size_of::<RawReportMut<'_>>(),
376            core::mem::size_of::<usize>()
377        );
378        assert_eq!(
379            core::mem::size_of::<Option<RawReportMut<'_>>>(),
380            core::mem::size_of::<usize>()
381        );
382        assert_eq!(
383            core::mem::size_of::<Result<(), RawReportMut<'_>>>(),
384            core::mem::size_of::<usize>()
385        );
386        assert_eq!(
387            core::mem::size_of::<Result<String, RawReportMut<'_>>>(),
388            core::mem::size_of::<String>()
389        );
390        assert_eq!(
391            core::mem::size_of::<Option<Option<RawReportMut<'_>>>>(),
392            core::mem::size_of::<Option<usize>>()
393        );
394    }
395
396    #[test]
397    fn test_raw_report_get_refs() {
398        let report = RawReport::new::<i32, HandlerI32>(789, vec![], vec![]);
399        let report_ref = report.as_ref();
400
401        // Accessing the pointer multiple times should be safe and consistent
402        let ptr1 = report_ref.as_ptr();
403        let ptr2 = report_ref.as_ptr();
404        assert_eq!(ptr1, ptr2);
405    }
406
407    #[test]
408    fn test_raw_report_clone_arc() {
409        // Test that Arc cloning maintains safety
410        let report = RawReport::new::<i32, HandlerI32>(123, vec![], vec![]);
411        let report_ref = report.as_ref();
412
413        assert_eq!(report_ref.strong_count(), 1);
414
415        // Original should have valid data
416        assert_eq!(report_ref.context_type_id(), TypeId::of::<i32>());
417
418        // Clone should work and maintain same type
419        // SAFETY: There are no assumptions on single ownership
420        let cloned = unsafe { report_ref.clone_arc() };
421        let cloned_ref = cloned.as_ref();
422
423        assert_eq!(report_ref.strong_count(), 2);
424        assert_eq!(cloned_ref.strong_count(), 2);
425
426        // Both should have same type and vtable
427        assert_eq!(report_ref.context_type_id(), cloned_ref.context_type_id());
428        assert!(core::ptr::eq(report_ref.vtable(), cloned_ref.vtable()));
429
430        core::mem::drop(cloned);
431
432        // After dropping the strong count should go back down
433        assert_eq!(report_ref.strong_count(), 1);
434    }
435
436    #[test]
437    fn test_raw_attachment_downcast() {
438        let int_report = RawReport::new::<i32, HandlerI32>(42, vec![], vec![]);
439        let string_report =
440            RawReport::new::<String, HandlerString>(String::from("test"), vec![], vec![]);
441
442        let int_ref = int_report.as_ref();
443        let string_ref = string_report.as_ref();
444
445        // Are TypeIds what we expect?
446        assert_eq!(int_ref.context_type_id(), TypeId::of::<i32>());
447        assert_eq!(string_ref.context_type_id(), TypeId::of::<String>());
448
449        // The vtables should be different
450        assert!(!core::ptr::eq(int_ref.vtable(), string_ref.vtable()));
451
452        // Cross-type downcasting should fail safely
453        assert!(int_ref.context_downcast::<String>().is_none());
454        assert!(string_ref.context_downcast::<i32>().is_none());
455
456        // Correct downcasting should work
457        assert_eq!(int_ref.context_downcast::<i32>().unwrap(), &42);
458        assert_eq!(string_ref.context_downcast::<String>().unwrap(), "test");
459    }
460
461    #[test]
462    fn test_raw_report_children() {
463        let child = RawReport::new::<i32, HandlerI32>(1, vec![], vec![]);
464        let parent = RawReport::new::<i32, HandlerI32>(0, vec![child], vec![]);
465
466        let parent_ref = parent.as_ref();
467        assert_eq!(parent_ref.context_downcast::<i32>().unwrap(), &0);
468
469        // Parent should have one child
470        let children = parent_ref.children();
471        assert_eq!(children.len(), 1);
472
473        // Child should be accessible safely
474        let child_ref = children[0].as_ref();
475        assert_eq!(child_ref.context_type_id(), TypeId::of::<i32>());
476        assert_eq!(child_ref.children().len(), 0);
477        assert_eq!(child_ref.context_downcast::<i32>().unwrap(), &1);
478
479        // Both should have same vtable (same type)
480        assert!(core::ptr::eq(parent_ref.vtable(), child_ref.vtable()));
481    }
482
483    #[test]
484    fn test_raw_report_with_attachments() {
485        use crate::{attachment::RawAttachment, handlers::AttachmentHandler};
486
487        // Create a simple attachment handler for i32
488        struct AttachmentHandlerI32;
489        impl AttachmentHandler<i32> for AttachmentHandlerI32 {
490            fn display(value: &i32, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
491                fmt::Display::fmt(value, formatter)
492            }
493            fn debug(value: &i32, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
494                fmt::Debug::fmt(value, formatter)
495            }
496        }
497
498        // Create some attachments
499        let attachment1 = RawAttachment::new::<i32, AttachmentHandlerI32>(100);
500        let attachment2 = RawAttachment::new::<i32, AttachmentHandlerI32>(200);
501
502        // Create a child report with one attachment
503        let child = RawReport::new::<i32, HandlerI32>(1, vec![], vec![attachment1]);
504
505        // Create a parent report with the child and another attachment
506        let parent = RawReport::new::<i32, HandlerI32>(0, vec![child], vec![attachment2]);
507
508        let parent_ref = parent.as_ref();
509        assert_eq!(parent_ref.context_downcast::<i32>().unwrap(), &0);
510
511        // Parent should have one child and one attachment
512        let children = parent_ref.children();
513        let attachments = parent_ref.attachments();
514        assert_eq!(children.len(), 1);
515        assert_eq!(attachments.len(), 1);
516
517        // Child should be accessible safely and have one attachment
518        let child_ref = children[0].as_ref();
519        assert_eq!(child_ref.context_type_id(), TypeId::of::<i32>());
520        assert_eq!(child_ref.context_downcast::<i32>().unwrap(), &1);
521        assert_eq!(child_ref.children().len(), 0);
522        assert_eq!(child_ref.attachments().len(), 1);
523
524        // Check attachment downcasting works
525        let parent_attachment_ref = attachments[0].as_ref();
526        let child_attachment_ref = child_ref.attachments()[0].as_ref();
527
528        assert_eq!(
529            parent_attachment_ref.attachment_type_id(),
530            TypeId::of::<i32>()
531        );
532        assert_eq!(
533            child_attachment_ref.attachment_type_id(),
534            TypeId::of::<i32>()
535        );
536
537        // Downcast attachments and verify values
538        assert_eq!(
539            *parent_attachment_ref.attachment_downcast::<i32>().unwrap(),
540            200
541        );
542        assert_eq!(
543            *child_attachment_ref.attachment_downcast::<i32>().unwrap(),
544            100
545        );
546
547        // Both reports should have same vtable (same context type)
548        assert!(core::ptr::eq(parent_ref.vtable(), child_ref.vtable()));
549    }
550
551    #[test]
552    fn test_raw_report_mut_basic() {
553        let mut report = RawReport::new::<i32, HandlerI32>(789, vec![], vec![]);
554
555        // SAFETY: We have unique ownership of the report
556        let mut report_mut = unsafe { report.as_mut() };
557
558        // Test that we can get a reference from the mutable reference
559        let report_ref = report_mut.as_ref();
560        assert_eq!(report_ref.context_type_id(), TypeId::of::<i32>());
561        assert_eq!(report_ref.context_downcast::<i32>().unwrap(), &789);
562
563        // Test reborrow functionality
564        let reborrowed = report_mut.reborrow();
565        let ref_from_reborrow = reborrowed.as_ref();
566        assert_eq!(ref_from_reborrow.context_type_id(), TypeId::of::<i32>());
567        assert_eq!(ref_from_reborrow.context_downcast::<i32>().unwrap(), &789);
568
569        // Test into_mut_ptr
570        let ptr = report_mut.into_mut_ptr();
571        assert!(!ptr.is_null());
572    }
573
574    #[test]
575    fn test_raw_report_mut_reborrow_lifetime() {
576        let mut report =
577            RawReport::new::<String, HandlerString>(String::from("test"), vec![], vec![]);
578
579        // SAFETY: We have unique ownership of the report
580        let mut report_mut = unsafe { report.as_mut() };
581
582        // Test that reborrow works with different lifetimes
583        {
584            let short_reborrow = report_mut.reborrow();
585            let ref_from_short = short_reborrow.as_ref();
586            assert_eq!(ref_from_short.context_downcast::<String>().unwrap(), "test");
587        }
588
589        // Original mutable reference should still be usable
590        let final_ref = report_mut.as_ref();
591        assert_eq!(final_ref.context_downcast::<String>().unwrap(), "test");
592    }
593
594    #[test]
595    fn test_raw_report_mut_with_children() {
596        let child = RawReport::new::<i32, HandlerI32>(1, vec![], vec![]);
597        let mut parent = RawReport::new::<i32, HandlerI32>(0, vec![child], vec![]);
598
599        // SAFETY: We have unique ownership of the parent report
600        let mut parent_mut = unsafe { parent.as_mut() };
601
602        let parent_ref = parent_mut.as_ref();
603        assert_eq!(parent_ref.context_downcast::<i32>().unwrap(), &0);
604
605        // Check that children are still accessible through the reference
606        let children = parent_ref.children();
607        assert_eq!(children.len(), 1);
608
609        let child_ref = children[0].as_ref();
610        assert_eq!(child_ref.context_downcast::<i32>().unwrap(), &1);
611
612        // Test reborrow with children
613        let reborrowed = parent_mut.reborrow();
614        let reborrow_ref = reborrowed.as_ref();
615        let reborrow_children = reborrow_ref.children();
616        assert_eq!(reborrow_children.len(), 1);
617        assert_eq!(
618            reborrow_children[0]
619                .as_ref()
620                .context_downcast::<i32>()
621                .unwrap(),
622            &1
623        );
624    }
625
626    #[test]
627    fn test_raw_report_mut_ptr_consistency() {
628        let mut report = RawReport::new::<i32, HandlerI32>(42, vec![], vec![]);
629
630        // Get immutable reference pointer first
631        let immut_ref = report.as_ref();
632        let immut_ptr = immut_ref.as_ptr() as usize;
633        // SAFETY: We have unique ownership of the report
634        let report_mut = unsafe { report.as_mut() };
635
636        // Get mutable pointer
637        let mut_ptr = report_mut.into_mut_ptr();
638
639        // Both pointers should point to the same location
640        assert_eq!(immut_ptr, mut_ptr as *const _ as usize);
641    }
642}