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