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