1use crate::SourceOffset;
2use std::{fmt::Display, hash::Hash, marker::PhantomData, ops::Add};
3
4#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize))]
9pub struct Span {
10 start: SourceOffset,
11 end: SourceOffset,
12}
13
14impl Span {
15 pub const DUMMY: Span = Span::new(SourceOffset::DUMMY, SourceOffset::DUMMY);
17 pub const ZERO: Span = Span::new(SourceOffset::ZERO, SourceOffset::ZERO);
18
19 #[inline]
23 pub const fn new(start: SourceOffset, end: SourceOffset) -> Self {
24 debug_assert!(start.0 <= end.0);
25 Self { start, end }
26 }
27
28 #[inline]
30 pub const fn start(&self) -> SourceOffset {
31 self.start
32 }
33
34 #[inline]
36 pub const fn end(&self) -> SourceOffset {
37 self.end
38 }
39
40 #[inline]
44 pub fn with_end(self, end: SourceOffset) -> Self {
45 debug_assert!(self.start <= end);
46 Self { start: self.start, end }
47 }
48
49 pub fn contains(&self, span: Span) -> bool {
51 self.start <= span.start && span.end <= self.end
52 }
53
54 pub fn overlaps(&self, span: Span) -> bool {
56 self.start < span.end && span.start < self.end
57 }
58
59 pub const fn is_empty(&self) -> bool {
61 self.start.0 == self.end.0
62 }
63
64 pub fn len(&self) -> u32 {
66 debug_assert!(self.start <= self.end);
67 self.end.0 - self.start.0
68 }
69
70 pub fn line_and_column(self, source: &'_ str) -> (u32, u32) {
72 let mut line = 0;
73 let mut column = 0;
74 let mut offset = self.start.0;
75 for char in source.chars() {
76 if offset == 0 {
77 break;
78 }
79 if char == '\n' {
80 column = 0;
81 line += 1;
82 } else {
83 column += 1;
84 }
85 offset -= char.len_utf8() as u32;
86 }
87 (line, column)
88 }
89}
90
91impl Add for Span {
94 type Output = Self;
95 fn add(self, rhs: Self) -> Self::Output {
96 if rhs == Span::DUMMY {
97 return self;
98 }
99 if self == Span::DUMMY {
100 return rhs;
101 }
102 let start = if self.start < rhs.start { self.start } else { rhs.start };
103 let end = if self.end > rhs.end { self.end } else { rhs.end };
104 Self { start, end }
105 }
106}
107
108impl Display for Span {
109 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110 write!(f, "[{}..{})", self.start.0, self.end.0)
111 }
112}
113
114#[cfg(feature = "miette")]
115impl From<Span> for miette::SourceSpan {
116 fn from(val: Span) -> Self {
117 Self::new(miette::SourceOffset::from(val.start.0 as usize), val.len() as usize)
118 }
119}
120
121impl<T: ToSpan> ToSpan for Vec<T> {
122 fn to_span(&self) -> Span {
123 let mut span = Span::ZERO;
124 for item in self {
125 if span == Span::ZERO {
126 span = item.to_span();
127 } else {
128 span = span + item.to_span()
129 }
130 }
131 span
132 }
133}
134
135impl<T: ToSpan, A: allocator_api2::alloc::Allocator> ToSpan for allocator_api2::vec::Vec<T, A> {
136 fn to_span(&self) -> Span {
137 let mut span = Span::ZERO;
138 for item in self {
139 if span == Span::ZERO {
140 span = item.to_span();
141 } else {
142 span = span + item.to_span()
143 }
144 }
145 span
146 }
147}
148
149#[cfg(feature = "bumpalo")]
150impl<'a, T: ToSpan> ToSpan for bumpalo::collections::Vec<'a, T> {
151 fn to_span(&self) -> Span {
152 let mut span = Span::ZERO;
153 for item in self {
154 if span == Span::ZERO {
155 span = item.to_span();
156 } else {
157 span = span + item.to_span()
158 }
159 }
160 span
161 }
162}
163
164macro_rules! impl_tuple {
165 ($len:tt: $($name:ident),+) => {
166 impl<$($name: ToSpan),+> ToSpan for ($($name),+) {
167 fn to_span(&self) -> Span {
168 self.0.to_span() + self.$len.to_span()
169 }
170 }
171 };
172}
173impl_tuple!(1: A, B);
174impl_tuple!(2: A, B, C);
175impl_tuple!(3: A, B, C, D);
176impl_tuple!(4: A, B, C, D, E);
177impl_tuple!(5: A, B, C, D, E, F);
178impl_tuple!(6: A, B, C, D, E, F, G);
179impl_tuple!(7: A, B, C, D, E, F, G, H);
180impl_tuple!(8: A, B, C, D, E, F, G, H, I);
181impl_tuple!(9: A, B, C, D, E, F, G, H, I, J);
182impl_tuple!(10: A, B, C, D, E, F, G, H, I, J, K);
183impl_tuple!(11: A, B, C, D, E, F, G, H, I, J, K, L);
184
185impl<T: ToSpan> ToSpan for Option<T> {
186 fn to_span(&self) -> Span {
187 self.as_ref().map_or(Span::DUMMY, |t| t.to_span())
188 }
189}
190
191impl<T> ToSpan for PhantomData<T> {
192 fn to_span(&self) -> Span {
193 Span::DUMMY
194 }
195}
196
197pub trait ToSpan {
200 fn to_span(&self) -> Span;
201}
202
203impl ToSpan for Span {
204 fn to_span(&self) -> Span {
205 *self
206 }
207}
208
209impl<T: ToSpan> ToSpan for &T {
210 fn to_span(&self) -> Span {
211 (**self).to_span()
212 }
213}
214
215impl<T: ToSpan> ToSpan for &mut T {
216 fn to_span(&self) -> Span {
217 (**self).to_span()
218 }
219}
220
221#[cfg(test)]
222mod test {
223 use super::*;
224
225 #[test]
226 fn test_span_vec() {
227 let mut vec = vec![];
228 vec.push(Span::new(SourceOffset(3), SourceOffset(10)));
229 vec.push(Span::new(SourceOffset(13), SourceOffset(15)));
230 assert_eq!(vec.to_span(), Span::new(SourceOffset(3), SourceOffset(15)));
231 }
232}