petr_utils/
sources.rs

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        // get any inner labels
86        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    /// goes from the `hi` of self to the `hi` of `after_span`
177    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    /// extends `self.hi` to the new offset.
191    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(Error, Debug)]
226    // struct ErrorWithSource<'a, T> where T: Diagnostic {
227    //     span: miette::SourceSpan,
228    //     source: &'a str,
229    //     diagnostic: T,
230    //     severity: miette::Severity,
231    //     help: Option<String>,
232    // }
233
234    // impl <T: Diagnostic> std::fmt::Display for ErrorWithSource<'_, T> {
235    //     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
236    //         todo!()
237    //     }
238    // }
239
240    // impl <'a, T: Diagnostic> Diagnostic for ErrorWithSource<'a, T> {
241    //     fn code<'b>(&'b self) -> Option<Box<dyn std::fmt::Display + 'b>> {
242    //         None
243    //     }
244
245    //     fn severity(&self) -> Option<miette::Severity> {
246    //         None
247    //     }
248
249    //     fn help<'b> (&'b self) -> Option<Box<dyn std::fmt::Display + 'b>> {
250    //         self.help.map(|x| -> Box<dyn std::fmt::Display> { Box::new(x) })
251    //     }
252
253    //     fn url<'b>(&'b self) -> Option<Box<dyn std::fmt::Display + 'b>> {
254    //         None
255    //     }
256
257    //     fn source_code(&self) -> Option<&dyn miette::SourceCode> {
258    //        Some(&self.source.to_string())
259    //     }
260
261    //     fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
262    //         let span = LabeledSpan::new_with_span(self., self.span)
263    //         Some(self.span)
264    //     }
265
266    //     fn related<'b>(&'b self) -> Option<Box<dyn Iterator<Item = &'b dyn Diagnostic> + 'b>> {
267    //         None
268    //     }
269
270    //     fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
271    //         None
272    //     }
273    //   }
274
275    //    pub type Source<'a> = &'a str;
276    #[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}