miette/eyreish/
wrapper.rs

1use 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}