rootcause/into_report.rs
1use crate::{markers, prelude::Report, report_collection::ReportCollection};
2
3/// Converts errors and reports into [`Report`] instances with specific
4/// thread-safety markers.
5///
6/// This trait is primarily used internally by the rootcause library for trait
7/// bounds in extension methods like
8/// [`ResultExt`](crate::result_ext::ResultExt). While it's available for direct
9/// use, most applications will find the [`report!`](crate::report!) macro more
10/// convenient for creating reports.
11///
12/// # Internal Usage
13///
14/// This trait enables generic conversions in methods like
15/// [`Result::context`](crate::result_ext::ResultExt::context), allowing them to
16/// accept various error types and convert them uniformly into reports.
17///
18/// # Automatic Implementations
19///
20/// This trait is automatically implemented for:
21/// - All types implementing [`std::error::Error`] (converts to new [`Report`])
22/// - Existing [`Report`] instances (performs identity or marker conversion)
23///
24/// # Thread Safety
25///
26/// The type parameter `T` specifies the desired thread-safety marker:
27/// - [`markers::SendSync`]: Report can be sent across threads
28/// - [`markers::Local`]: Report is restricted to the current thread
29///
30/// When converting from [`SendSync`](markers::SendSync) to
31/// [`Local`](markers::Local), the conversion always succeeds. Converting from
32/// [`Local`](markers::Local) to [`SendSync`](markers::SendSync) is only
33/// available if the context type is `Send + Sync`.
34///
35/// # Typical Usage
36///
37/// Most applications won't need to call this trait directly. Instead, consider:
38/// - Using [`report!`](crate::report!) to create reports from errors or strings
39/// - Using [`ResultExt`](crate::result_ext::ResultExt) methods to add context
40/// to `Result` types
41/// - Using the `From` trait for generic type conversions
42///
43/// # Examples
44///
45/// Direct usage is possible, though the alternatives above are often more
46/// ergonomic:
47///
48/// ```
49/// use std::io;
50///
51/// use rootcause::{IntoReport, prelude::*};
52///
53/// // Direct usage
54/// let error: io::Error = io::Error::new(io::ErrorKind::NotFound, "file not found");
55/// let report: Report<io::Error> = error.into_report();
56///
57/// // Alternative using the macro (often more convenient)
58/// let error2: io::Error = io::Error::new(io::ErrorKind::NotFound, "config.toml");
59/// let report2: Report<io::Error> = report!(error2);
60/// ```
61pub trait IntoReport<T> {
62 /// The context type of the resulting report.
63 type Context: ?Sized + 'static;
64
65 /// The ownership marker of the resulting report.
66 type Ownership: 'static;
67
68 /// Converts `self` into a [`Report`] with the specified thread-safety
69 /// marker.
70 ///
71 /// Most applications will find the [`report!`](crate::report!) macro more
72 /// convenient for creating reports.
73 #[track_caller]
74 #[must_use]
75 fn into_report(self) -> Report<Self::Context, Self::Ownership, T>;
76}
77
78impl<C: ?Sized, O> IntoReport<markers::SendSync> for Report<C, O, markers::SendSync> {
79 type Context = C;
80 type Ownership = O;
81
82 #[inline(always)]
83 fn into_report(self) -> Report<Self::Context, Self::Ownership, markers::SendSync> {
84 self
85 }
86}
87
88impl<C: ?Sized, O, T> IntoReport<markers::Local> for Report<C, O, T> {
89 type Context = C;
90 type Ownership = O;
91
92 #[inline(always)]
93 fn into_report(self) -> Report<Self::Context, Self::Ownership, markers::Local> {
94 self.into_local()
95 }
96}
97
98impl<C: Sized + 'static, T> IntoReport<T> for C
99where
100 C: markers::ObjectMarkerFor<T> + core::error::Error,
101{
102 type Context = C;
103 type Ownership = markers::Mutable;
104
105 #[inline(always)]
106 fn into_report(self) -> Report<C, markers::Mutable, T> {
107 Report::new(self)
108 }
109}
110
111/// Converts errors and reports into [`ReportCollection`] instances.
112///
113/// This trait is primarily used internally by the rootcause library for trait
114/// bounds. While it's available for direct use, most applications will find the
115/// `From` trait or iterator methods more convenient for creating collections of
116/// reports.
117///
118/// # Internal Usage
119///
120/// This trait provides trait bounds for generic conversions to
121/// [`ReportCollection`], similar to how [`IntoReport`] works for single
122/// reports.
123///
124/// # Automatic Implementations
125///
126/// This trait is automatically implemented for:
127/// - All types implementing [`std::error::Error`] (creates single-item
128/// collection)
129/// - [`Report`] instances (creates single-item collection)
130/// - [`ReportCollection`] instances (identity or marker conversion)
131///
132/// # Typical Usage
133///
134/// Most applications won't need to call this trait directly. Instead, consider:
135/// - Using iterator methods: `iter.map(|e| report!(e)).collect()`
136/// - Using `From` trait implementations for type conversions
137/// - Using [`ReportCollection::new()`] or builder methods
138///
139/// # Examples
140///
141/// Direct usage is possible, though the alternatives above are often more
142/// ergonomic:
143///
144/// ```
145/// use std::io;
146///
147/// use rootcause::{IntoReportCollection, prelude::*, report_collection::ReportCollection};
148///
149/// // Direct usage
150/// let error: io::Error = io::Error::other("An error occurred");
151/// let collection: ReportCollection<io::Error> = error.into_report_collection();
152/// assert_eq!(collection.len(), 1);
153///
154/// // Alternative using iterators (often more convenient for multiple errors)
155/// let errors: Vec<io::Error> = vec![io::Error::other("error 1")];
156/// let collection2: ReportCollection = errors.into_iter().map(|e| report!(e)).collect();
157/// ```
158pub trait IntoReportCollection<T> {
159 /// The context type of the resulting report collection.
160 type Context: ?Sized + 'static;
161
162 /// Converts `self` into a [`ReportCollection`] with the specified
163 /// thread-safety marker.
164 ///
165 /// Most applications will find iterator methods or the `From` trait more
166 /// convenient for creating collections.
167 #[track_caller]
168 #[must_use]
169 fn into_report_collection(self) -> ReportCollection<Self::Context, T>;
170}
171
172impl<C, O> IntoReportCollection<markers::SendSync> for Report<C, O, markers::SendSync>
173where
174 C: ?Sized,
175 O: markers::ReportOwnershipMarker,
176{
177 type Context = C;
178
179 #[inline(always)]
180 fn into_report_collection(self) -> ReportCollection<Self::Context, markers::SendSync> {
181 core::iter::once(self).collect()
182 }
183}
184
185impl<C, O, T> IntoReportCollection<markers::Local> for Report<C, O, T>
186where
187 C: ?Sized,
188 O: markers::ReportOwnershipMarker,
189{
190 type Context = C;
191
192 #[inline(always)]
193 fn into_report_collection(self) -> ReportCollection<Self::Context, markers::Local> {
194 core::iter::once(self.into_local()).collect()
195 }
196}
197
198impl<C> IntoReportCollection<markers::SendSync> for ReportCollection<C, markers::SendSync>
199where
200 C: ?Sized,
201{
202 type Context = C;
203
204 #[inline(always)]
205 fn into_report_collection(self) -> ReportCollection<Self::Context, markers::SendSync> {
206 self
207 }
208}
209
210impl<C, T> IntoReportCollection<markers::Local> for ReportCollection<C, T>
211where
212 C: ?Sized,
213{
214 type Context = C;
215
216 #[inline(always)]
217 fn into_report_collection(self) -> ReportCollection<Self::Context, markers::Local> {
218 self.into_local()
219 }
220}
221
222impl<C, T> IntoReportCollection<T> for C
223where
224 C: markers::ObjectMarkerFor<T> + core::error::Error,
225{
226 type Context = C;
227
228 #[inline(always)]
229 fn into_report_collection(self) -> ReportCollection<C, T> {
230 core::iter::once(Report::new(self)).collect()
231 }
232}