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.saturating_add(offset) }
68 }
69
70 pub fn backward(&self, offset: usize) -> Self {
80 Self { source: self.source, offset: self.offset.saturating_sub(offset) }
81 }
82
83 pub fn range_for(&self, length: usize) -> Range<usize> {
84 self.offset..self.offset.saturating_add(length)
85 }
86}
87
88impl Span {
89 pub fn new(start: Position, end: Position) -> Self {
90 debug_assert!(
91 start.source.0.is_empty() || end.source.0.is_empty() || start.source == end.source,
92 "span start and end must be in the same file",
93 );
94
95 Self { start, end }
96 }
97
98 pub fn dummy(start_offset: usize, end_offset: usize) -> Self {
99 Self::new(Position::dummy(start_offset), Position::dummy(end_offset))
100 }
101
102 pub fn between(start: Span, end: Span) -> Self {
103 start.join(end)
104 }
105
106 pub fn join(self, other: Span) -> Span {
107 Span::new(self.start, other.end)
108 }
109
110 pub fn contains(&self, position: &impl HasPosition) -> bool {
111 self.has_offset(position.offset())
112 }
113
114 pub fn has_offset(&self, offset: usize) -> bool {
115 self.start.offset <= offset && offset <= self.end.offset
116 }
117
118 pub fn to_range(&self) -> Range<usize> {
119 self.start.offset..self.end.offset
120 }
121
122 pub fn to_tuple(&self) -> (usize, usize) {
123 (self.start.offset, self.end.offset)
124 }
125
126 pub fn length(&self) -> usize {
127 self.end.offset - self.start.offset
128 }
129
130 pub fn with_start(&self, start: Position) -> Span {
131 Span::new(start, self.end)
132 }
133
134 pub fn with_end(&self, end: Position) -> Span {
135 Span::new(self.start, end)
136 }
137
138 pub fn subspan(&self, start: usize, end: usize) -> Span {
139 Span::new(self.start.forward(start), self.start.forward(end))
140 }
141
142 pub fn is_before(&self, other: impl HasPosition) -> bool {
143 self.end.offset <= other.position().offset
144 }
145
146 pub fn is_after(&self, other: impl HasPosition) -> bool {
147 self.start.offset >= other.position().offset
148 }
149}
150
151impl HasPosition for Position {
152 fn position(&self) -> Position {
153 *self
154 }
155}
156
157impl HasSource for Position {
158 fn source(&self) -> SourceIdentifier {
159 self.source
160 }
161}
162
163impl HasSpan for Span {
164 fn span(&self) -> Span {
165 *self
166 }
167}
168
169impl<T> HasSpan for &T
170where
171 T: HasSpan,
172{
173 fn span(&self) -> Span {
174 (*self).span()
175 }
176}
177
178impl<T: HasSpan> HasPosition for T {
179 fn position(&self) -> Position {
180 self.start_position()
181 }
182}
183
184impl HasSource for Span {
185 fn source(&self) -> SourceIdentifier {
186 self.start.source
187 }
188}
189
190impl HasSource for dyn HasPosition {
191 fn source(&self) -> SourceIdentifier {
192 self.position().source()
193 }
194}
195
196impl HasSource for dyn HasSpan {
197 fn source(&self) -> SourceIdentifier {
198 self.span().source()
199 }
200}
201
202impl<T: HasSpan> HasSpan for Box<T> {
203 fn span(&self) -> Span {
204 self.as_ref().span()
205 }
206}
207
208impl From<Position> for usize {
209 fn from(position: Position) -> usize {
210 position.offset
211 }
212}
213
214impl From<&Position> for usize {
215 fn from(position: &Position) -> usize {
216 position.offset
217 }
218}
219
220impl From<Span> for Range<usize> {
221 fn from(span: Span) -> Range<usize> {
222 Range { start: span.start.into(), end: span.end.into() }
223 }
224}
225
226impl From<&Span> for Range<usize> {
227 fn from(span: &Span) -> Range<usize> {
228 Range { start: span.start.into(), end: span.end.into() }
229 }
230}
231
232impl std::fmt::Display for Position {
233 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234 write!(f, "{}", self.offset)
235 }
236}
237
238impl std::fmt::Display for Span {
239 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
240 write!(f, "{}-{}", self.start, self.end)
241 }
242}
243
244impl From<Position> for SourceIdentifier {
245 fn from(position: Position) -> SourceIdentifier {
246 position.source
247 }
248}
249
250impl From<&Position> for SourceIdentifier {
251 fn from(position: &Position) -> SourceIdentifier {
252 position.source
253 }
254}
255
256impl From<Span> for SourceIdentifier {
257 fn from(span: Span) -> SourceIdentifier {
258 span.start.source
259 }
260}
261
262impl From<&Span> for SourceIdentifier {
263 fn from(span: &Span) -> SourceIdentifier {
264 span.start.source
265 }
266}
267
268impl std::ops::Add<usize> for Position {
269 type Output = Position;
270
271 fn add(self, rhs: usize) -> Self::Output {
272 self.forward(rhs)
273 }
274}
275
276impl std::ops::Sub<usize> for Position {
277 type Output = Position;
278
279 fn sub(self, rhs: usize) -> Self::Output {
280 self.backward(rhs)
281 }
282}
283
284impl std::ops::AddAssign<usize> for Position {
285 fn add_assign(&mut self, rhs: usize) {
286 self.offset += rhs;
287 }
288}
289
290impl std::ops::SubAssign<usize> for Position {
291 fn sub_assign(&mut self, rhs: usize) {
292 self.offset -= rhs;
293 }
294}