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