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