1#[derive(Copy, Clone, PartialEq, Debug)]
3pub struct Span {
4 pub start: u32,
6 pub end: u32,
8}
9
10#[derive(Clone, PartialEq, Debug)]
11pub struct SpanWithSource<T> {
12 pub span: Span,
14 pub src: T,
16}
17
18impl Span {
19 pub fn new(start: u32, end: u32) -> Span {
21 Span { start, end }
22 }
23
24 pub fn with_src<T>(self, src: T) -> SpanWithSource<T> {
26 SpanWithSource { span: self, src }
27 }
28
29 pub fn extend_end(self, end: u32) -> Span {
32 Span {
33 start: self.start,
34 end: self.end.max(end),
35 }
36 }
37
38 pub fn next_window(self, len: u32) -> Span {
41 Span {
42 start: self.end,
43 end: self.end + len,
44 }
45 }
46
47 pub fn overlap(self, other: Span) -> Option<Span> {
49 let start = self.start.max(other.start);
50 let end = self.end.min(other.end);
51 (start <= end).then_some(Span { start, end })
52 }
53}
54
55impl<'a> SpanWithSource<&'a str> {
56 pub fn as_str(&self) -> &'a str {
58 &self.src[self.span.start as usize..self.span.end as usize]
59 }
60}
61
62impl<T: AsRef<str>> SpanWithSource<T> {
63 pub fn contextual_formatter(&self) -> impl '_ + std::fmt::Display {
64 SpanWithSourceContextualFormatter(self)
65 }
66}
67
68impl<T> std::fmt::Display for SpanWithSource<T>
69where
70 T: AsRef<str>,
71{
72 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73 let src = self.src.as_ref();
74 let s = &src[self.span.start as usize..(self.span.end as usize).clamp(0, src.len())];
75 write!(f, "{s}")
76 }
77}
78
79struct SpanWithSourceContextualFormatter<'a, T>(&'a SpanWithSource<T>);
80
81impl<'a, T> std::fmt::Display for SpanWithSourceContextualFormatter<'a, T>
82where
83 T: AsRef<str>,
84{
85 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86 let span = self.0.span;
87 let src = self.0.src.as_ref();
88
89 let mut current_span = Span::new(0, 0);
90 writeln!(f, "Source:")?;
91 for (idx, line) in src.split('\n').enumerate() {
92 current_span = current_span.next_window(1 + line.len() as u32);
93 if current_span.overlap(span).is_some() {
94 let line_number = idx + 1;
95 let line_src = current_span.with_src(src);
96 write!(f, "{line_number:3}: {line_src}")?;
97 }
98 }
99 Ok(())
100 }
101}