1use std::ops::Range;
2
3use mago_source::HasSource;
4use serde::Deserialize;
5use serde::Serialize;
6
7use mago_source::SourceIdentifier;
8
9#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
10#[repr(C)]
11pub struct Position {
12 pub source: SourceIdentifier,
13 pub offset: usize,
14}
15
16#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
17#[repr(C)]
18pub struct Span {
19 pub start: Position,
20 pub end: Position,
21}
22
23pub trait HasPosition {
24 fn position(&self) -> Position;
25
26 #[inline]
27 fn offset(&self) -> usize {
28 self.position().offset
29 }
30}
31
32pub trait HasSpan {
33 fn span(&self) -> Span;
34
35 fn start_position(&self) -> Position {
36 self.span().start
37 }
38
39 fn end_position(&self) -> Position {
40 self.span().end
41 }
42}
43
44impl Position {
45 pub fn new(source: SourceIdentifier, offset: usize) -> Self {
46 Self { source, offset }
47 }
48
49 pub fn dummy(offset: usize) -> Self {
50 Self::new(SourceIdentifier::dummy(), offset)
51 }
52
53 pub fn start_of(source: SourceIdentifier) -> Self {
54 Self::new(source, 0)
55 }
56
57 pub const fn forward(&self, offset: usize) -> Self {
67 Self { source: self.source, offset: self.offset + offset }
68 }
69
70 pub fn backward(&self, offset: usize) -> Self {
80 Self { source: self.source, offset: self.offset - offset }
81 }
82
83 pub fn range_for(&self, length: usize) -> Range<usize> {
84 self.offset..self.offset + length
85 }
86}
87
88impl Span {
89 pub fn new(start: Position, end: Position) -> Self {
90 debug_assert!(start.source == end.source, "span start and end must be in the same file");
91
92 Self { start, end }
93 }
94
95 pub fn between(start: Span, end: Span) -> Self {
96 start.join(end)
97 }
98
99 pub fn join(self, other: Span) -> Span {
100 Span::new(self.start, other.end)
101 }
102
103 pub fn contains(&self, position: &impl HasPosition) -> bool {
104 self.has_offset(position.offset())
105 }
106
107 pub fn has_offset(&self, offset: usize) -> bool {
108 self.start.offset <= offset && offset <= self.end.offset
109 }
110
111 pub fn to_range(&self) -> Range<usize> {
112 self.start.offset..self.end.offset
113 }
114
115 pub fn to_tuple(&self) -> (usize, usize) {
116 (self.start.offset, self.end.offset)
117 }
118
119 pub fn length(&self) -> usize {
120 self.end.offset - self.start.offset
121 }
122
123 pub fn with_start(&self, start: Position) -> Span {
124 Span::new(start, self.end)
125 }
126
127 pub fn with_end(&self, end: Position) -> Span {
128 Span::new(self.start, end)
129 }
130
131 pub fn subspan(&self, start: usize, end: usize) -> Span {
132 Span::new(self.start.forward(start), self.start.forward(end))
133 }
134
135 pub fn is_before(&self, other: impl HasPosition) -> bool {
136 self.end.offset <= other.position().offset
137 }
138
139 pub fn is_after(&self, other: impl HasPosition) -> bool {
140 self.start.offset >= other.position().offset
141 }
142}
143
144impl HasPosition for Position {
145 fn position(&self) -> Position {
146 *self
147 }
148}
149
150impl HasSource for Position {
151 fn source(&self) -> SourceIdentifier {
152 self.source
153 }
154}
155
156impl HasSpan for Span {
157 fn span(&self) -> Span {
158 *self
159 }
160}
161
162impl<T> HasSpan for &T
163where
164 T: HasSpan,
165{
166 fn span(&self) -> Span {
167 (*self).span()
168 }
169}
170
171impl<T: HasSpan> HasPosition for T {
172 fn position(&self) -> Position {
173 self.start_position()
174 }
175}
176
177impl HasSource for Span {
178 fn source(&self) -> SourceIdentifier {
179 self.start.source
180 }
181}
182
183impl HasSource for dyn HasPosition {
184 fn source(&self) -> SourceIdentifier {
185 self.position().source()
186 }
187}
188
189impl HasSource for dyn HasSpan {
190 fn source(&self) -> SourceIdentifier {
191 self.span().source()
192 }
193}
194
195impl<T: HasSpan> HasSpan for Box<T> {
196 fn span(&self) -> Span {
197 self.as_ref().span()
198 }
199}
200
201impl From<Position> for usize {
202 fn from(position: Position) -> usize {
203 position.offset
204 }
205}
206
207impl From<&Position> for usize {
208 fn from(position: &Position) -> usize {
209 position.offset
210 }
211}
212
213impl From<Span> for Range<usize> {
214 fn from(span: Span) -> Range<usize> {
215 Range { start: span.start.into(), end: span.end.into() }
216 }
217}
218
219impl From<&Span> for Range<usize> {
220 fn from(span: &Span) -> Range<usize> {
221 Range { start: span.start.into(), end: span.end.into() }
222 }
223}
224
225impl std::fmt::Display for Position {
226 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
227 write!(f, "{}", self.offset)
228 }
229}
230
231impl std::fmt::Display for Span {
232 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
233 write!(f, "{}-{}", self.start, self.end)
234 }
235}
236
237impl From<Position> for SourceIdentifier {
238 fn from(position: Position) -> SourceIdentifier {
239 position.source
240 }
241}
242
243impl From<&Position> for SourceIdentifier {
244 fn from(position: &Position) -> SourceIdentifier {
245 position.source
246 }
247}
248
249impl From<Span> for SourceIdentifier {
250 fn from(span: Span) -> SourceIdentifier {
251 span.start.source
252 }
253}
254
255impl From<&Span> for SourceIdentifier {
256 fn from(span: &Span) -> SourceIdentifier {
257 span.start.source
258 }
259}
260
261impl std::ops::Add<usize> for Position {
262 type Output = Position;
263
264 fn add(self, rhs: usize) -> Self::Output {
265 self.forward(rhs)
266 }
267}
268
269impl std::ops::Sub<usize> for Position {
270 type Output = Position;
271
272 fn sub(self, rhs: usize) -> Self::Output {
273 self.backward(rhs)
274 }
275}
276
277impl std::ops::AddAssign<usize> for Position {
278 fn add_assign(&mut self, rhs: usize) {
279 self.offset += rhs;
280 }
281}
282
283impl std::ops::SubAssign<usize> for Position {
284 fn sub_assign(&mut self, rhs: usize) {
285 self.offset -= rhs;
286 }
287}