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