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