Skip to main content

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}