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
//! Individual attachments for error reports.
//!
//! This module provides types for creating and working with individual
//! attachments that can be added to error reports. Attachments allow you to
//! include additional context, data, or information alongside the main error
//! message.
//!
//! # Core Types
//!
//! - [`ReportAttachment`]: An owned attachment that can be added to a report
//! - [`ReportAttachmentRef`]: A reference to an attachment, typically obtained
//! from a report
//!
//! # Creating Attachments
//!
//! Attachments can be created using the [`ReportAttachment::new`] method or
//! the [`report_attachment!`] macro:
//!
//! ```
//! use rootcause::{markers::Dynamic, prelude::*, report_attachment::ReportAttachment};
//!
//! // Create a simple string attachment
//! let attachment: ReportAttachment<&str> = ReportAttachment::new("Additional information");
//! let attachment: ReportAttachment<Dynamic> = report_attachment!("Additional information");
//!
//! // Create an attachment from a custom type using Debug formatting
//! #[derive(Debug, Clone)]
//! struct AttachmentData {
//! file: String,
//! line: u32,
//! }
//!
//! let attachment_data = AttachmentData {
//! file: "main.rs".to_string(),
//! line: 42,
//! };
//! let debug_attachment: ReportAttachment<AttachmentData> =
//! ReportAttachment::new_custom::<handlers::Debug>(attachment_data.clone());
//! let debug_attachment: ReportAttachment<AttachmentData> = report_attachment!(attachment_data);
//! ```
//!
//! # Type Parameters
//!
//! Both types have the same generic parameters:
//!
//! - **Attachment type**: Can be a concrete type or [`Dynamic`] for type
//! erasure
//! - **Thread safety**: [`SendSync`] (default) for thread-safe attachments, or
//! [`Local`] for single-threaded use
//!
//! ```
//! use std::rc::Rc;
//!
//! use rootcause::{markers, prelude::*, report_attachment::ReportAttachment};
//!
//! // Send + Sync attachment (default)
//! let attachment: ReportAttachment<String, markers::SendSync> =
//! ReportAttachment::new("Thread-safe data".to_string());
//!
//! // Local-only attachment (cannot be sent between threads)
//! let local_data = Rc::new("Local data".to_string());
//! let local_attachment: ReportAttachment<Rc<String>, markers::Local> =
//! ReportAttachment::new(local_data);
//!
//! // Send + Sync attachments can be converted to local-only
//! let local_conversion: ReportAttachment<String, markers::Local> = attachment.into_local();
//! ```
//!
//! # Attachment References
//!
//! When attachments are stored in reports, you typically work with
//! [`ReportAttachmentRef`] which provides access to the attachment data without
//! taking ownership:
//!
//! ```
//! use rootcause::{prelude::*, report_attachment::ReportAttachment};
//!
//! let attachment = ReportAttachment::new("Important context");
//! let mut report = report!("An error occurred");
//! report.attachments_mut().push(attachment.into_dynamic());
//!
//! // Access the attachment through a reference
//! let attachment_ref = report.attachments().get(0).unwrap();
//! println!("Attachment: {}", attachment_ref);
//! ```
//!
//! # Type Erasure and Downcasting
//!
//! Attachments support type erasure through [`Dynamic`], allowing collections
//! of different attachment types. You can downcast back to concrete types when
//! needed:
//!
//! [`Dynamic`]: crate::markers::Dynamic
//!
//! ```
//! use std::any::TypeId;
//!
//! use rootcause::{markers::Dynamic, prelude::*, report_attachment::ReportAttachment};
//!
//! let attachment: ReportAttachment<&str> = ReportAttachment::new("text data");
//! let erased: ReportAttachment<Dynamic> = attachment.into_dynamic();
//!
//! // Check the type at runtime
//! assert_eq!(erased.inner_type_id(), TypeId::of::<&str>());
//!
//! // Safely downcast back to the original type
//! let attachment_ref = erased.as_ref();
//! let typed_ref = attachment_ref.downcast_attachment::<&str>().unwrap();
//! ```
//!
//! # Formatting and Handlers
//!
//! Attachments use handlers to control how they are formatted in reports.
//! The default handler uses the [`Display`] handler, but you can also use
//! the [`Debug`] handler or create your own:
//!
//! ```
//! use rootcause::{prelude::*, report_attachment::ReportAttachment};
//!
//! #[derive(Debug)]
//! struct MyData {
//! value: i32,
//! }
//!
//! // Use Display formatting (default)
//! let display_attachment: ReportAttachment<&str> = ReportAttachment::new("text");
//!
//! // Use Debug formatting explicitly
//! let debug_attachment: ReportAttachment<MyData> =
//! ReportAttachment::new_custom::<handlers::Debug>(MyData { value: 42 });
//!
//! struct MyOtherData {
//! value: i32,
//! }
//!
//! struct MyDataHandler;
//! impl rootcause::handlers::AttachmentHandler<MyOtherData> for MyDataHandler {
//! fn debug(data: &MyOtherData, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
//! write!(f, "MyOtherData value is: {}", data.value)
//! }
//!
//! fn display(data: &MyOtherData, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
//! write!(f, "MyOtherData value is: {}", data.value)
//! }
//! }
//!
//! let custom_attachment: ReportAttachment<MyOtherData> =
//! ReportAttachment::new_custom::<MyDataHandler>(MyOtherData { value: 100 });
//! ```
//!
//! [`SendSync`]: crate::markers::SendSync
//! [`Local`]: crate::markers::Local
//! [`Display`]: crate::handlers::Display
//! [`Debug`]: crate::handlers::Debug
pub use ;