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