miette/eyreish/
wrapper.rs1use core::fmt::{self, Debug, Display};
2
3use std::error::Error as StdError;
4
5use crate::{Diagnostic, LabeledSpan, Report, SourceCode};
6
7use crate as miette;
8
9#[repr(transparent)]
10pub(crate) struct DisplayError<M>(pub(crate) M);
11
12impl<M> Debug for DisplayError<M>
13where
14 M: Display,
15{
16 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17 Display::fmt(&self.0, f)
18 }
19}
20
21impl<M> Display for DisplayError<M>
22where
23 M: Display,
24{
25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 Display::fmt(&self.0, f)
27 }
28}
29
30impl<M> StdError for DisplayError<M> where M: Display + 'static {}
31impl<M> Diagnostic for DisplayError<M> where M: Display + 'static {}
32
33#[repr(transparent)]
34pub(crate) struct MessageError<M>(pub(crate) M);
35
36impl<M> Debug for MessageError<M>
37where
38 M: Display + Debug,
39{
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 Debug::fmt(&self.0, f)
42 }
43}
44
45impl<M> Display for MessageError<M>
46where
47 M: Display + Debug,
48{
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 Display::fmt(&self.0, f)
51 }
52}
53
54impl<M> StdError for MessageError<M> where M: Display + Debug + 'static {}
55impl<M> Diagnostic for MessageError<M> where M: Display + Debug + 'static {}
56
57#[repr(transparent)]
58pub(crate) struct BoxedError(pub(crate) Box<dyn Diagnostic + Send + Sync>);
59
60impl Diagnostic for BoxedError {
61 fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
62 self.0.code()
63 }
64
65 fn severity(&self) -> Option<miette::Severity> {
66 self.0.severity()
67 }
68
69 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
70 self.0.help()
71 }
72
73 fn url<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
74 self.0.url()
75 }
76
77 fn labels<'a>(&'a self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + 'a>> {
78 self.0.labels()
79 }
80
81 fn source_code(&self) -> Option<&dyn miette::SourceCode> {
82 self.0.source_code()
83 }
84
85 fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
86 self.0.related()
87 }
88
89 fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
90 self.0.diagnostic_source()
91 }
92}
93
94impl Debug for BoxedError {
95 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96 Debug::fmt(&self.0, f)
97 }
98}
99
100impl Display for BoxedError {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 Display::fmt(&self.0, f)
103 }
104}
105
106impl StdError for BoxedError {
107 fn source(&self) -> Option<&(dyn StdError + 'static)> {
108 self.0.source()
109 }
110
111 fn description(&self) -> &str {
112 #[allow(deprecated)]
113 self.0.description()
114 }
115
116 fn cause(&self) -> Option<&dyn StdError> {
117 #[allow(deprecated)]
118 self.0.cause()
119 }
120}
121
122pub(crate) struct WithSourceCode<E, C> {
123 pub(crate) error: E,
124 pub(crate) source_code: C,
125}
126
127impl<E: Diagnostic, C: SourceCode> Diagnostic for WithSourceCode<E, C> {
128 fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
129 self.error.code()
130 }
131
132 fn severity(&self) -> Option<miette::Severity> {
133 self.error.severity()
134 }
135
136 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
137 self.error.help()
138 }
139
140 fn url<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
141 self.error.url()
142 }
143
144 fn labels<'a>(&'a self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + 'a>> {
145 self.error.labels()
146 }
147
148 fn source_code(&self) -> Option<&dyn miette::SourceCode> {
149 self.error.source_code().or(Some(&self.source_code))
150 }
151
152 fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
153 self.error.related()
154 }
155
156 fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
157 self.error.diagnostic_source()
158 }
159}
160
161impl<C: SourceCode> Diagnostic for WithSourceCode<Report, C> {
162 fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
163 self.error.code()
164 }
165
166 fn severity(&self) -> Option<miette::Severity> {
167 self.error.severity()
168 }
169
170 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
171 self.error.help()
172 }
173
174 fn url<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
175 self.error.url()
176 }
177
178 fn labels<'a>(&'a self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + 'a>> {
179 self.error.labels()
180 }
181
182 fn source_code(&self) -> Option<&dyn miette::SourceCode> {
183 self.error.source_code().or(Some(&self.source_code))
184 }
185
186 fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
187 self.error.related()
188 }
189
190 fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
191 self.error.diagnostic_source()
192 }
193}
194
195impl<E: Debug, C> Debug for WithSourceCode<E, C> {
196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197 Debug::fmt(&self.error, f)
198 }
199}
200
201impl<E: Display, C> Display for WithSourceCode<E, C> {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 Display::fmt(&self.error, f)
204 }
205}
206
207impl<E: StdError, C> StdError for WithSourceCode<E, C> {
208 fn source(&self) -> Option<&(dyn StdError + 'static)> {
209 self.error.source()
210 }
211}
212
213impl<C> StdError for WithSourceCode<Report, C> {
214 fn source(&self) -> Option<&(dyn StdError + 'static)> {
215 self.error.source()
216 }
217}
218
219#[cfg(test)]
220mod tests {
221 use thiserror::Error;
222
223 use crate::{Diagnostic, LabeledSpan, Report, SourceCode, SourceSpan};
224
225 #[derive(Error, Debug)]
226 #[error("inner")]
227 struct Inner {
228 pub(crate) at: SourceSpan,
229 pub(crate) source_code: Option<String>,
230 }
231
232 impl Diagnostic for Inner {
233 fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
234 Some(Box::new(std::iter::once(LabeledSpan::underline(self.at))))
235 }
236
237 fn source_code(&self) -> Option<&dyn SourceCode> {
238 self.source_code.as_ref().map(|s| s as _)
239 }
240 }
241
242 #[test]
243 fn no_override() {
244 let inner_source = "hello world";
245 let outer_source = "abc";
246
247 let report = Report::from(Inner {
248 at: (0..5).into(),
249 source_code: Some(inner_source.to_string()),
250 })
251 .with_source_code(outer_source.to_string());
252
253 let underlined = String::from_utf8(
254 report
255 .source_code()
256 .unwrap()
257 .read_span(&(0..5).into(), 0, 0)
258 .unwrap()
259 .data()
260 .to_vec(),
261 )
262 .unwrap();
263 assert_eq!(underlined, "hello");
264 }
265
266 #[test]
267 #[cfg(feature = "fancy")]
268 fn two_source_codes() {
269 #[derive(Error, Debug)]
270 #[error("outer")]
271 struct Outer {
272 pub(crate) errors: Vec<Inner>,
273 }
274
275 impl Diagnostic for Outer {
276 fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
277 Some(Box::new(self.errors.iter().map(|e| e as _)))
278 }
279 }
280
281 let inner_source = "hello world";
282 let outer_source = "abc";
283
284 let report = Report::from(Outer {
285 errors: vec![
286 Inner {
287 at: (0..5).into(),
288 source_code: Some(inner_source.to_string()),
289 },
290 Inner {
291 at: (1..2).into(),
292 source_code: None,
293 },
294 ],
295 })
296 .with_source_code(outer_source.to_string());
297
298 let message = format!("{:?}", report);
299 assert!(message.contains(inner_source));
300 assert!(message.contains(outer_source));
301 }
302}