rootcause 0.12.1

A flexible, ergonomic, and inspectable error reporting library for Rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
//! Marker types and traits for defining ownership and thread-safety semantics.
//!
//! This module provides type-level markers that control how reports and
//! attachments behave with respect to ownership, cloning, and thread safety.
//! These markers are used as generic parameters in types like [`Report<C, O,
//! T>`](crate::Report) to encode compile-time guarantees about how data can be
//! accessed and shared.
//!
//! # Design Philosophy
//!
//! The constraints encoded by these markers are enforced at construction time.
//! It is impossible to construct a [`Report`](crate::Report),
//! [`ReportRef`], or
//! [`ReportCollection`] that violates the invariants
//! associated with its marker types. This means you can trust that a
//! `Report<_, _, SendSync>` truly is `Send + Sync`, and that a `Report<_,
//! Mutable>` truly has unique ownership.
//!
//! [`ReportRef`]: crate::ReportRef
//! [`ReportCollection`]: crate::report_collection::ReportCollection
//!
//! # Context Marker
//!
//! The context marker is the first type parameter (`C`) in [`Report<C, O,
//! T>`](crate::Report) and determines what type of error is stored at the root:
//!
//! - [`Dynamic`]: Type-erased context - the root error can be any type (like
//!   [`anyhow::Error`]). This is the default when you write just [`Report`].
//! - Any concrete type: Typed context - the root error must be that specific
//!   type (like [`error-stack`]).
//!
//! See the [`Dynamic`] documentation for a detailed comparison and usage
//! examples.
//!
//! [`anyhow::Error`]: https://docs.rs/anyhow
//! [`error-stack`]: https://docs.rs/error-stack
//! [`Report`]: crate::Report
//!
//! # Ownership Markers
//!
//! Ownership markers control whether reports and report references can be
//! mutated and cloned.
//!
//! For owned reports ([`Report<C, O, T>`](crate::Report)), the ownership marker
//! `O` can be:
//! - [`Mutable`]: Unique ownership - the report can be mutated but not cloned
//! - [`Cloneable`]: Shared ownership - the report can be cloned but not mutated
//!
//! For report references ([`ReportRef<C, O>`](crate::ReportRef)), the ownership
//! marker `O` can be:
//! - [`Cloneable`]: Enables [`clone_arc`](crate::ReportRef::clone_arc) to get
//!   an owned report
//! - [`Uncloneable`]: Does not provide
//!   [`clone_arc`](crate::ReportRef::clone_arc)
//!
//! # Thread Safety Markers
//!
//! Thread safety markers control whether reports can be sent between threads or
//! shared across threads. These appear as the third type parameter (`T`) in
//! [`Report<C, O, T>`](crate::Report):
//!
//! - [`SendSync`]: The report and all its contents are `Send + Sync`, allowing
//!   the report to cross thread boundaries.
//! - [`Local`]: The report contains non-thread-safe data (like `Rc` or raw
//!   pointers) and cannot be sent between threads.
//!
//! # Examples
//!
//! ## Creating Reports with Different Ownership Semantics
//!
//! ```
//! use rootcause::prelude::*;
//!
//! // Mutable report - can be modified by adding context and attachments
//! let mut report: Report<String, markers::Mutable> = report!("Error".to_string());
//! let report: Report<String, markers::Mutable> = report.attach("Additional context");
//!
//! // Convert to cloneable report - can be cloned but not mutated
//! let cloneable: Report<String, markers::Cloneable> = report.into_cloneable();
//! let cloned: Report<String, markers::Cloneable> = cloneable.clone();
//! assert_eq!(format!("{}", cloneable), format!("{}", cloned));
//! ```
//!
//! ## Working with Thread Safety
//!
//! ```
//! use std::rc::Rc;
//!
//! use rootcause::prelude::*;
//!
//! // Thread-safe report with String (String is Send + Sync)
//! let thread_safe: Report<String, markers::Mutable, markers::SendSync> =
//!     report!("Thread-safe error".to_string());
//!
//! // Can be sent to another thread
//! std::thread::spawn(move || {
//!     println!("{}", thread_safe);
//! });
//!
//! // Local report with Rc (Rc is !Send + !Sync)
//! let local_data: Rc<String> = Rc::new("Not thread-safe".to_string());
//! let local_report: Report<Rc<String>, markers::Mutable, markers::Local> = report!(local_data);
//! // local_report cannot be sent to another thread - won't compile
//! ```

use crate::ReportMut;

/// Marker type for reports with dynamic (type-erased) context.
///
/// `Dynamic` is used as the context type parameter in
/// [`Report<Dynamic, O, T>`](crate::Report) to indicate that the actual error
/// type at the root is not known at compile time. This is the default behavior
/// and is similar to [`anyhow::Error`] - any error type
/// can be stored and the `?` operator works automatically.
///
/// # Key Properties
///
/// - **Zero-cost type erasure**: No actual instance of `Dynamic` is ever
///   stored. It's purely a marker type used at the type level.
/// - **Automatic conversions**: The `?` operator automatically converts any
///   error type into `Report<Dynamic>`.
/// - **Flexible propagation**: When you just need errors to propagate upward
///   with context, `Dynamic` is the right choice.
///
/// # When to Use Dynamic
///
/// Use `Report<Dynamic>` (or just [`Report`](crate::Report), which defaults to
/// `Dynamic`) when:
/// - You're propagating errors upward and don't need to pattern match on
///   specific error types
/// - You want the flexibility to return different error types from the same
///   function
/// - You're building applications where error handling is about logging and
///   displaying, not recovery
///
/// Use `Report<YourErrorType>` instead when:
/// - Callers need to pattern match on specific error variants for recovery
/// - You want compile-time guarantees about which errors a function can return
/// - You're building libraries with well-defined error types
///
/// See [`examples/typed_reports.rs`] for when typed errors are appropriate.
///
/// [`examples/typed_reports.rs`]: https://github.com/rootcause-rs/rootcause/blob/main/examples/typed_reports.rs
///
/// # Examples
///
/// ## Basic Usage
///
/// ```
/// use rootcause::prelude::*;
///
/// // Report<Dynamic> is the default - these are equivalent:
/// fn might_fail() -> Result<(), Report> {
///     // Can return any error type via ?
///     std::fs::read_to_string("/nonexistent")?;
///     Ok(())
/// }
///
/// fn might_fail_explicit() -> Result<(), Report<markers::Dynamic>> {
///     std::fs::read_to_string("/nonexistent")?;
///     Ok(())
/// }
/// ```
///
/// ## Mixing Error Types
///
/// ```
/// use std::{fs, io};
///
/// use rootcause::prelude::*;
///
/// #[derive(Debug)]
/// struct ConfigError(String);
/// impl std::fmt::Display for ConfigError {
///     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
///         write!(f, "Config error: {}", self.0)
///     }
/// }
/// impl std::error::Error for ConfigError {}
///
/// fn load_and_parse(path: &str) -> Result<u32, Report> {
///     // io::Error from fs operations
///     let contents = fs::read_to_string(path)?;
///
///     // Custom ConfigError
///     if contents.is_empty() {
///         Err(ConfigError("empty file".into()))?;
///     }
///
///     // ParseIntError from parse
///     let value: u32 = contents.trim().parse()?;
///
///     Ok(value)
/// }
/// ```
///
/// ## Converting Between Typed and Dynamic
///
/// ```
/// use rootcause::prelude::*;
///
/// #[derive(Debug)]
/// struct MyError;
/// impl std::fmt::Display for MyError {
///     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
///         write!(f, "MyError")
///     }
/// }
/// impl std::error::Error for MyError {}
///
/// fn typed_error() -> Result<(), Report<MyError>> {
///     Err(report!(MyError))
/// }
///
/// fn dynamic_error() -> Result<(), Report> {
///     // Typed report automatically coerces to dynamic via ?
///     typed_error()?;
///     Ok(())
/// }
///
/// // Or convert explicitly:
/// fn explicit_conversion() -> Result<(), Report> {
///     let typed: Report<MyError> = report!(MyError);
///     let dynamic: Report = typed.into_dynamic();
///     Err(dynamic)
/// }
/// ```
///
/// See [`examples/error_coercion.rs`] for a complete guide to type conversions.
///
/// [`examples/error_coercion.rs`]: https://github.com/rootcause-rs/rootcause/blob/main/examples/error_coercion.rs
pub struct Dynamic {
    /// This field ensures `Dynamic` is an unsized type.
    ///
    /// Since the `Dynamic` type itself is never instantiated, the choice
    /// of an unsized type here is purely to enforce that property at the type
    /// level.
    _phantom_unsized: [()],
}

/// Marker type for owned reports with unique ownership.
///
/// This marker is used exclusively with [`Report<C, Mutable, T>`] (not
/// [`ReportRef`]). It indicates that the report has unique ownership of its
/// data, which allows mutation operations but prevents cloning.
///
/// [`Report<C, Mutable, T>`]: crate::Report
/// [`ReportRef`]: crate::ReportRef
///
/// # Available Operations
///
/// With `Mutable` ownership, you can:
/// - Add attachments with [`attach`](crate::Report::attach)
/// - Add parent context with [`context`](crate::Report::context)
/// - Get mutable access with [`as_mut`](crate::Report::as_mut)
/// - Convert to [`Cloneable`] with
///   [`into_cloneable`](crate::Report::into_cloneable)
///
/// # Examples
///
/// ```
/// use rootcause::prelude::*;
///
/// let report: Report<String, markers::Mutable> = report!("Database error".to_string());
///
/// // Can add attachments (consumes and returns the report)
/// let report: Report<String, markers::Mutable> = report.attach("connection timeout");
///
/// // Can add parent context
/// let report: Report<String, markers::Mutable> =
///     report.context("Failed to fetch user data".to_string());
///
/// // Cannot clone - Mutable reports don't implement Clone
/// // let cloned = report.clone(); // ❌ Won't compile
/// ```
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Default, Hash)]
pub struct Mutable;

/// Marker type for cloneable reports and report references.
///
/// This marker is used with both [`Report<C, Cloneable, T>`](crate::Report) and
/// [`ReportRef<C, Cloneable>`](crate::ReportRef). It indicates shared ownership
/// using reference counting (`Arc` internally), which allows cheap cloning but
/// prevents mutation.
///
/// # Usage with Report
///
/// For [`Report<C, Cloneable, T>`](crate::Report), this marker means the report
/// itself implements `Clone`, allowing you to cheaply clone the entire report
/// (shallow copy via `Arc`).
///
/// # Usage with ReportRef
///
/// For [`ReportRef<C, Cloneable>`](crate::ReportRef), the marker enables the
/// [`clone_arc`](crate::ReportRef::clone_arc) method, which clones the
/// underlying `Arc` to produce an owned [`Report<C, Cloneable,
/// T>`](crate::Report). Note that `ReportRef` itself is always `Copy` and
/// `Clone` regardless of the ownership marker - the `Cloneable`
/// marker specifically enables converting the reference back to an owned
/// report.
///
/// # When to Use
///
/// Use `Cloneable` reports when you need to:
/// - Share an error report across multiple code paths
/// - Store reports in collections that require `Clone`
/// - Return the same error from multiple places without deep copying
///
/// # Converting to Cloneable
///
/// Convert a [`Mutable`] report to `Cloneable` using
/// [`into_cloneable`](crate::Report::into_cloneable):
///
/// ```
/// use rootcause::prelude::*;
///
/// let report: Report<String, markers::Mutable> = report!("Error".to_string());
///
/// // Convert to cloneable
/// let cloneable: Report<String, markers::Cloneable> = report.into_cloneable();
///
/// // Now can clone cheaply (shallow clone via Arc)
/// let clone1: Report<String, markers::Cloneable> = cloneable.clone();
/// let clone2: Report<String, markers::Cloneable> = cloneable.clone();
/// ```
///
/// # Examples
///
/// Cloning owned reports:
///
/// ```
/// use rootcause::prelude::*;
///
/// fn process_error(error: Report<String, markers::Cloneable>) {
///     // Can clone the error to pass to multiple handlers
///     let for_logging = error.clone();
///     let for_metrics = error.clone();
///
///     println!("Logging: {}", for_logging);
///     println!("Metrics: {}", for_metrics);
/// }
///
/// let report: Report<String> = report!("An error occurred".to_string());
/// process_error(report.into_cloneable());
/// ```
///
/// Using `clone_arc` on report references:
///
/// ```
/// use rootcause::{ReportRef, prelude::*};
///
/// let report: Report<String, markers::Cloneable> = report!("Error".to_string()).into_cloneable();
///
/// // Get a reference (ReportRef is Copy, so this is cheap)
/// let report_ref: ReportRef<String, markers::Cloneable> = report.as_ref();
///
/// // Clone the underlying Arc to get an owned Report
/// let owned: Report<String, markers::Cloneable> = report_ref.clone_arc();
/// ```
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Default, Hash)]
pub struct Cloneable;

/// Marker type for non-cloneable report references.
///
/// This marker is used exclusively with [`ReportRef<C,
/// Uncloneable>`](crate::ReportRef) (not [`Report`](crate::Report)). It
/// indicates that the reference does not provide the
/// [`clone_arc`](crate::ReportRef::clone_arc) method to obtain an owned report.
///
/// Note that `ReportRef` itself is always `Copy` and `Clone` - you can always
/// copy the reference itself. The `Uncloneable` marker only prevents cloning
/// the underlying `Arc` to get an owned `Report`.
///
/// # Common Uses
///
/// `Uncloneable` references typically arise in two situations:
///
/// 1. **Taking a reference to a `Mutable` report**: When you call
///    [`as_ref`](crate::Report::as_ref) on a `Report<C, Mutable>`, you get a
///    `ReportRef<C, Uncloneable>` because the underlying report has unique
///    ownership.
///
/// 2. **Explicitly restricting cloneability**: You can convert a `ReportRef<C,
///    Cloneable>` to `ReportRef<C, Uncloneable>` when you want to pass a
///    reference that explicitly cannot use `clone_arc`, ensuring the recipient
///    can only inspect the report without obtaining ownership. This can be
///    useful in APIs that need to accept both cloneable and uncloneable
///    references.
///
///
/// # Examples
///
/// Taking a reference to a `Mutable` report:
///
/// ```
/// use rootcause::{ReportRef, prelude::*};
///
/// let report: Report<String, markers::Mutable> = report!("An error occurred".to_string());
///
/// // Taking a reference to a Mutable report gives an Uncloneable reference
/// let report_ref: ReportRef<String, markers::Uncloneable> = report.as_ref();
///
/// // The reference itself can be copied (ReportRef is Copy)
/// let copy = report_ref;
///
/// // But you cannot clone the underlying Arc to get an owned Report
/// // let owned = report_ref.clone_arc(); // ❌ Method not available
/// ```
///
/// Explicitly converting to `Uncloneable`:
///
/// ```
/// use rootcause::{ReportRef, prelude::*};
///
/// let report: Report<String, markers::Cloneable> = report!("Error".to_string()).into_cloneable();
///
/// let cloneable_ref: ReportRef<String, markers::Cloneable> = report.as_ref();
///
/// // Convert to uncloneable to restrict the recipient's ability to clone
/// let uncloneable_ref: ReportRef<String, markers::Uncloneable> = cloneable_ref.into_uncloneable();
/// ```
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Default, Hash)]
pub struct Uncloneable;

/// Marker type indicating that a report and all its contents are `Send + Sync`.
///
/// This is the default thread-safety marker for reports. When all the context
/// objects and attachments in a report implement `Send + Sync`, the report
/// itself can be safely sent to other threads and shared between threads.
///
/// # When to Use
///
/// Most standard types in Rust are `Send + Sync`, including:
/// - Primitive types (`i32`, `String`, `Vec`, etc.)
/// - Most standard library types
/// - Types explicitly designed for concurrent use
///
/// Use `SendSync` (the default) unless you have a specific need for
/// thread-local data.
///
/// # Examples
///
/// ```
/// use std::thread;
///
/// use rootcause::prelude::*;
///
/// // String is Send + Sync, so the report is too
/// let report: Report<String, markers::Mutable, markers::SendSync> =
///     report!("Thread-safe error".to_string());
///
/// // Can send to another thread
/// thread::spawn(move || {
///     println!("Error in thread: {}", report);
/// })
/// .join()
/// .unwrap();
/// ```
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Default, Hash)]
pub struct SendSync;

/// Marker type indicating that a report is not `Send` or `Sync`.
///
/// This marker is used when a report contains thread-local data that cannot be
/// safely sent between threads or shared across threads. Common examples
/// include `Rc`, raw pointers, or types that explicitly opt out of
/// `Send`/`Sync`.
///
/// # When to Use
///
/// Use `Local` when your error context or attachments contain:
/// - `Rc<T>` or `Weak<T>` (use `Arc<T>` for thread-safe alternative)
/// - Raw pointers (`*const T`, `*mut T`)
/// - Types that wrap thread-local storage
/// - Any type that is `!Send` or `!Sync`
///
/// # Converting to Local
///
/// You can convert a thread-safe report to a local one using
/// [`into_local`](crate::Report::into_local), or create a local report directly
/// when the context type is not `Send + Sync`.
///
/// # Examples
///
/// ```
/// use std::rc::Rc;
///
/// use rootcause::prelude::*;
///
/// // Rc is not Send or Sync, so the report must be Local
/// let local_data: Rc<String> = Rc::new("Thread-local error".to_string());
/// let report: Report<Rc<String>, markers::Mutable, markers::Local> = report!(local_data);
///
/// // This report cannot be sent to another thread
/// // std::thread::spawn(move || {
/// //     println!("{}", report); // ❌ Won't compile
/// // });
/// ```
///
/// Converting a thread-safe report to local:
///
/// ```
/// use std::rc::Rc;
///
/// use rootcause::prelude::*;
///
/// let report: Report<String> = report!("Error".to_string());
///
/// // Convert to local report so we can use thread-local data
/// let local_report: Report<String, markers::Mutable, markers::Local> = report.into_local();
/// ```
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Default, Hash)]
pub struct Local;

mod sealed_report_ownership_marker {
    use super::*;

    pub trait Sealed: 'static {}

    impl Sealed for Mutable {}
    impl Sealed for Cloneable {}
}

mod sealed_object_marker {
    pub trait Sealed: 'static {}

    impl<C: Sized + 'static> Sealed for C {}
}

/// Marker trait for ownership semantics of owned reports.
///
/// This trait is implemented for [`Mutable`] and [`Cloneable`], the two
/// ownership markers that can be used with [`Report<C, O, T>`](crate::Report).
/// It exists primarily to enable the associated type `RefMarker`, which
/// determines the ownership marker used when creating a reference to the
/// report.
///
/// # Relationship to Report Construction
///
/// While this trait defines the ownership modes, the actual enforcement of
/// ownership invariants happens at report construction time. It is impossible
/// to construct a `Report<_, Mutable>` that doesn't have unique ownership, or a
/// `Report<_, Cloneable>` that hasn't been properly set up for shared
/// ownership.
///
/// # Ownership Modes
///
/// - [`Mutable`]: Indicates unique ownership. The report can be mutated but not
///   cloned. Methods like [`attach`](crate::Report::attach) and
///   [`as_mut`](crate::Report::as_mut) are only available in this mode.
///
/// - [`Cloneable`]: Indicates shared ownership via reference counting. The
///   report can be cloned cheaply but cannot be mutated since there may be
///   multiple references.
///
/// # Associated Types
///
/// The `RefMarker` associated type determines what kind of reference you get
/// when calling [`as_ref`](crate::Report::as_ref):
/// - For [`Mutable`], this is [`Uncloneable`] (the reference cannot use
///   `clone_arc`)
/// - For [`Cloneable`], this is [`Cloneable`] (the reference can use
///   `clone_arc`)
///
/// # Implementation
///
/// This trait is sealed and cannot be implemented outside of this crate. You
/// should use the provided implementations for [`Mutable`] and [`Cloneable`].
pub trait ReportOwnershipMarker: sealed_report_ownership_marker::Sealed {
    /// The ownership marker for references to this report type.
    ///
    /// This determines what type of reference is created when you call
    /// [`Report::as_ref`](crate::Report::as_ref):
    ///
    /// - For [`Mutable`]: Returns [`ReportRef<C,
    ///   Uncloneable>`](crate::ReportRef) because the underlying report has
    ///   unique ownership
    /// - For [`Cloneable`]: Returns [`ReportRef<C,
    ///   Cloneable>`](crate::ReportRef) because the underlying report already
    ///   uses shared ownership
    type RefMarker;
}
impl ReportOwnershipMarker for Mutable {
    type RefMarker = Uncloneable;
}
impl ReportOwnershipMarker for Cloneable {
    type RefMarker = Cloneable;
}

/// Marker trait combining object and thread-safety requirements.
///
/// This trait enforces thread-safety constraints for context and attachment
/// data at report construction time. Reports can only be constructed when their
/// context and attachments satisfy the requirements of the thread-safety
/// marker.
///
/// # Implementations
///
/// - For `T = Local`: Implemented for all `Sized + 'static` types, regardless
///   of their `Send`/`Sync` status. This allows using types like `Rc` in local
///   reports.
///
/// - For `T = SendSync`: Implemented only for `Sized + 'static` types that are
///   also `Send + Sync`. This ensures thread-safe reports can only be
///   constructed with thread-safe data.
///
/// # Enforcement at Construction
///
/// The key insight is that this trait is used as a bound during report
/// construction. You cannot create a `Report<C, _, SendSync>` unless `C:
/// ObjectMarkerFor<SendSync>`, which requires `C: Send + Sync`. This makes it
/// impossible to accidentally create an invalid report:
///
/// ```compile_fail
/// use std::rc::Rc;
/// use rootcause::prelude::*;
///
/// // This won't compile because Rc is not Send + Sync
/// let rc_data: Rc<String> = Rc::new("error".to_string());
/// let report: Report<Rc<String>, markers::Mutable, markers::SendSync> = report!(rc_data);
/// ```
///
/// Use [`Local`] instead for non-thread-safe data:
///
/// ```
/// use std::rc::Rc;
///
/// use rootcause::prelude::*;
///
/// let rc_data: Rc<String> = Rc::new("error".to_string());
/// let report: Report<Rc<String>, markers::Mutable, markers::Local> = report!(rc_data);
/// ```
//
// # Safety:
//
// This trait is sealed and cannot be implemented outside of this crate. It is
// guaranteed to only be implemented for the combinations of types and
// thread-safety markers listed above.
pub trait ObjectMarkerFor<T>: sealed_object_marker::Sealed + Sized + 'static {
    /// Runs report creation hooks specific to this thread-safety marker.
    #[doc(hidden)]
    #[track_caller]
    fn run_creation_hooks(report: ReportMut<'_, Dynamic, T>);
}

impl<O: Sized + 'static> ObjectMarkerFor<Local> for O {
    #[inline(always)]
    fn run_creation_hooks(report: ReportMut<'_, Dynamic, Local>) {
        crate::hooks::report_creation::run_creation_hooks_local(report);
    }
}

impl<O: Sized + 'static> ObjectMarkerFor<SendSync> for O
where
    O: Send + Sync,
{
    #[inline(always)]
    fn run_creation_hooks(report: ReportMut<'_, Dynamic, SendSync>) {
        crate::hooks::report_creation::run_creation_hooks_sendsync(report);
    }
}