1use error_printing::SourcedItem;
2use miette::{Diagnostic, LabeledSpan, SourceSpan};
3
4#[derive(PartialEq, Eq, Clone)]
5pub struct SpannedItem<T>(T, Span);
6
7impl<T: PartialOrd> PartialOrd for SpannedItem<T> {
8 fn partial_cmp(
9 &self,
10 other: &Self,
11 ) -> Option<std::cmp::Ordering> {
12 self.0.partial_cmp(&other.0)
13 }
14}
15
16impl<T: Ord> Ord for SpannedItem<T> {
17 fn cmp(
18 &self,
19 other: &Self,
20 ) -> std::cmp::Ordering {
21 self.0.cmp(&other.0)
22 }
23}
24
25impl<T> SpannedItem<T> {
26 pub fn item(&self) -> &T {
27 &self.0
28 }
29
30 pub fn into_item(self) -> T {
31 self.0
32 }
33
34 pub fn span(&self) -> Span {
35 self.1
36 }
37
38 fn with_source(
39 self,
40 name: impl Into<String>,
41 source: &'static str,
42 ) -> SourcedItem<SpannedItem<T>>
43 where
44 T: Diagnostic,
45 {
46 SourcedItem::new(name, source, self)
47 }
48
49 pub fn map<B>(
50 self,
51 f: impl Fn(T) -> B,
52 ) -> SpannedItem<B> {
53 SpannedItem(f(self.0), self.1)
54 }
55}
56
57impl<T: std::fmt::Display> std::fmt::Display for SpannedItem<T> {
58 fn fmt(
59 &self,
60 f: &mut std::fmt::Formatter<'_>,
61 ) -> std::fmt::Result {
62 write!(f, "{}", self.item())
63 }
64}
65impl<T: std::error::Error> std::error::Error for SpannedItem<T> {}
66
67impl<T: Diagnostic + std::error::Error> Diagnostic for SpannedItem<T> {
68 fn code<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
69 self.item().code()
70 }
71
72 fn severity(&self) -> Option<miette::Severity> {
73 self.item().severity()
74 }
75
76 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
77 self.item().help()
78 }
79
80 fn url<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
81 self.item().url()
82 }
83
84 fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
85 let item = self.item();
87 let labels = item.labels().unwrap_or_else(|| Box::new(std::iter::empty()));
88 let span = self.span().span();
89 let label = self.item().to_string();
90 let labeled_span = LabeledSpan::new_with_span(Some(label), span);
91 Some(Box::new(labels.chain(std::iter::once(labeled_span))))
92 }
93
94 fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
95 self.item().related()
96 }
97
98 fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
99 self.item().diagnostic_source()
100 }
101}
102
103impl<T> Copy for SpannedItem<T> where T: Copy {}
104
105impl<T> std::fmt::Debug for SpannedItem<T>
106where
107 T: std::fmt::Debug,
108{
109 fn fmt(
110 &self,
111 f: &mut std::fmt::Formatter<'_>,
112 ) -> std::fmt::Result {
113 write!(f, "SpannedItem {:?} [{:?}]", self.0, self.1)
114 }
115}
116
117#[derive(PartialEq, Eq, Clone, Copy, Debug)]
118pub struct SourceId(usize);
119
120impl From<usize> for SourceId {
121 fn from(other: usize) -> SourceId {
122 SourceId(other)
123 }
124}
125impl From<SourceId> for usize {
126 fn from(other: SourceId) -> usize {
127 other.0
128 }
129}
130
131#[derive(PartialEq, Eq, Clone, Copy, Debug)]
132pub struct Span {
133 source: SourceId,
134 span: miette::SourceSpan,
135}
136
137impl Span {
138 pub fn new(
139 source: SourceId,
140 span: miette::SourceSpan,
141 ) -> Self {
142 Self { source, span }
143 }
144
145 pub fn with_item<T>(
146 self,
147 item: T,
148 ) -> SpannedItem<T> {
149 SpannedItem(item, self)
150 }
151
152 pub fn join(
153 &self,
154 after_span: Span,
155 ) -> Span {
156 assert!(self.source == after_span.source, "cannot join spans from different files");
157
158 let (first_span, second_span) = if self.span.offset() < after_span.span.offset() {
159 (self.span, after_span.span)
160 } else {
161 (after_span.span, self.span)
162 };
163
164 let (first_end, second_end) = (first_span.len() + first_span.offset(), second_span.len() + second_span.offset());
165
166 let end = std::cmp::max(first_end, second_end);
167
168 let length = end - first_span.offset();
169
170 Self {
171 source: self.source,
172 span: SourceSpan::new(first_span.offset().into(), length.into()),
173 }
174 }
175
176 pub fn hi_to_hi(
178 &self,
179 after_span: Span,
180 ) -> Self {
181 assert!(self.source == after_span.source, "cannot join spans from different files");
182 let lo = self.span.offset() + self.span.len();
183 let hi = after_span.span.offset() + after_span.span.len();
184 Self {
185 source: self.source,
186 span: SourceSpan::new(lo.into(), (hi - lo).into()),
187 }
188 }
189
190 pub fn extend(
192 &self,
193 hi: usize,
194 ) -> Self {
195 assert!(hi >= self.span().offset(), "cannot extend a span to a lower offset");
196 let lo = self.span.offset();
197 Self {
198 source: self.source,
199 span: SourceSpan::new(lo.into(), (hi - lo).into()),
200 }
201 }
202
203 pub fn span(&self) -> miette::SourceSpan {
204 self.span
205 }
206
207 pub fn source(&self) -> SourceId {
208 self.source
209 }
210
211 pub fn zero_length(&self) -> Self {
212 Span {
213 source: self.source,
214 span: SourceSpan::new(self.span.offset().into(), 0.into()),
215 }
216 }
217}
218
219pub mod error_printing {
220
221 use miette::{Diagnostic, LabeledSpan, Report};
222
223 use crate::{IndexMap, SourceId, SpannedItem};
224
225 #[derive(Debug)]
277 pub(crate) struct SourcedItem<T>
278 where
279 T: Diagnostic + std::error::Error + std::fmt::Debug,
280 {
281 source: miette::NamedSource,
282 item: T,
283 }
284 impl<T: Diagnostic> SourcedItem<T> {
285 pub(crate) fn new(
286 name: impl Into<String>,
287 source: &'static str,
288 item: SpannedItem<T>,
289 ) -> SourcedItem<SpannedItem<T>> {
290 SourcedItem {
291 source: miette::NamedSource::new(name.into(), source),
292 item,
293 }
294 }
295 }
296
297 impl<T: Diagnostic> Diagnostic for SourcedItem<T> {
298 fn code<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
299 self.item.code()
300 }
301
302 fn severity(&self) -> Option<miette::Severity> {
303 self.item.severity()
304 }
305
306 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
307 self.item.help()
308 }
309
310 fn url<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
311 self.item.url()
312 }
313
314 fn source_code(&self) -> Option<&dyn miette::SourceCode> {
315 Some(&self.source)
316 }
317
318 fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
319 self.item.labels()
320 }
321
322 fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
323 self.item.related()
324 }
325
326 fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
327 self.item.diagnostic_source()
328 }
329 }
330
331 impl<T> std::fmt::Display for SourcedItem<T>
332 where
333 T: Diagnostic + std::error::Error + std::fmt::Debug,
334 {
335 fn fmt(
336 &self,
337 f: &mut std::fmt::Formatter<'_>,
338 ) -> std::fmt::Result {
339 write!(f, "{}", self.item)
340 }
341 }
342
343 impl<T: std::error::Error> std::error::Error for SourcedItem<T> where T: Diagnostic + std::error::Error + std::fmt::Debug {}
344
345 pub fn render<T>(
346 sources: &IndexMap<SourceId, (&'static str, &'static str)>,
347 err: SpannedItem<T>,
348 ) -> Report
349 where
350 T: miette::Diagnostic + Send + Sync + 'static,
351 {
352 let span = err.span();
353 let (name, source) = sources.get(span.source());
354 let sourced_item = err.with_source(*name, source);
355 Report::new(sourced_item)
356 }
357}