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