1use std::ops::Bound;
9use std::ops::Range;
10use std::ops::RangeBounds;
11
12use serde::Deserialize;
13use serde::Serialize;
14
15use mago_database::file::FileId;
16use mago_database::file::HasFileId;
17
18#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
20#[repr(transparent)]
21pub struct Position {
22 pub offset: u32,
23}
24
25#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
30pub struct Span {
31 pub file_id: FileId,
33 pub start: Position,
35 pub end: Position,
37}
38
39pub trait HasPosition {
41 fn position(&self) -> Position;
43
44 #[inline]
46 fn offset(&self) -> u32 {
47 self.position().offset
48 }
49}
50
51pub trait HasSpan {
53 fn span(&self) -> Span;
55
56 #[inline]
58 fn start_position(&self) -> Position {
59 self.span().start
60 }
61
62 #[inline]
64 fn start_offset(&self) -> u32 {
65 self.start_position().offset
66 }
67
68 #[inline]
70 fn end_position(&self) -> Position {
71 self.span().end
72 }
73
74 #[inline]
76 fn end_offset(&self) -> u32 {
77 self.end_position().offset
78 }
79}
80
81impl Position {
82 #[inline]
84 #[must_use]
85 pub const fn new(offset: u32) -> Self {
86 Self { offset }
87 }
88
89 #[inline]
91 #[must_use]
92 pub const fn zero() -> Self {
93 Self { offset: 0 }
94 }
95
96 #[inline]
98 #[must_use]
99 pub const fn is_zero(&self) -> bool {
100 self.offset == 0
101 }
102
103 #[inline]
107 #[must_use]
108 pub const fn forward(&self, offset: u32) -> Self {
109 Self { offset: self.offset.saturating_add(offset) }
110 }
111
112 #[inline]
116 #[must_use]
117 pub const fn backward(&self, offset: u32) -> Self {
118 Self { offset: self.offset.saturating_sub(offset) }
119 }
120
121 #[inline]
123 #[must_use]
124 pub const fn range_for(&self, length: u32) -> Range<u32> {
125 self.offset..self.offset.saturating_add(length)
126 }
127}
128
129impl Span {
130 #[inline]
137 #[must_use]
138 pub const fn new(file_id: FileId, start: Position, end: Position) -> Self {
139 Self { file_id, start, end }
140 }
141
142 #[inline]
144 #[must_use]
145 pub const fn zero() -> Self {
146 Self { file_id: FileId::zero(), start: Position::zero(), end: Position::zero() }
147 }
148
149 #[inline]
151 #[must_use]
152 pub fn dummy(start_offset: u32, end_offset: u32) -> Self {
153 Self::new(FileId::zero(), Position::new(start_offset), Position::new(end_offset))
154 }
155
156 #[inline]
159 #[must_use]
160 pub fn between(start: Span, end: Span) -> Self {
161 start.join(end)
162 }
163
164 #[inline]
166 #[must_use]
167 pub const fn is_zero(&self) -> bool {
168 self.start.is_zero() && self.end.is_zero()
169 }
170
171 #[inline]
174 #[must_use]
175 pub fn join(self, other: Span) -> Span {
176 Span::new(self.file_id, self.start, other.end)
177 }
178
179 #[inline]
182 #[must_use]
183 pub fn to_end(&self, end: Position) -> Span {
184 Span::new(self.file_id, self.start, end)
185 }
186
187 #[inline]
190 #[must_use]
191 pub fn from_start(&self, start: Position) -> Span {
192 Span::new(self.file_id, start, self.end)
193 }
194
195 #[inline]
198 #[must_use]
199 pub fn subspan(&self, start: u32, end: u32) -> Span {
200 Span::new(self.file_id, self.start.forward(start), self.start.forward(end))
201 }
202
203 #[inline]
205 pub fn contains(&self, position: &impl HasPosition) -> bool {
206 self.has_offset(position.offset())
207 }
208
209 #[inline]
211 #[must_use]
212 pub fn has_offset(&self, offset: u32) -> bool {
213 self.start.offset <= offset && offset <= self.end.offset
214 }
215
216 #[inline]
218 #[must_use]
219 pub fn to_range(&self) -> Range<u32> {
220 self.start.offset..self.end.offset
221 }
222
223 #[inline]
225 #[must_use]
226 pub fn to_range_usize(&self) -> Range<usize> {
227 let start = self.start.offset as usize;
228 let end = self.end.offset as usize;
229
230 start..end
231 }
232
233 #[inline]
235 #[must_use]
236 pub fn to_offset_tuple(&self) -> (u32, u32) {
237 (self.start.offset, self.end.offset)
238 }
239
240 #[inline]
242 #[must_use]
243 pub fn length(&self) -> u32 {
244 self.end.offset.saturating_sub(self.start.offset)
245 }
246
247 #[inline]
248 pub fn is_before(&self, other: &impl HasPosition) -> bool {
249 self.end.offset <= other.position().offset
250 }
251
252 #[inline]
253 pub fn is_after(&self, other: &impl HasPosition) -> bool {
254 self.start.offset >= other.position().offset
255 }
256}
257
258impl HasPosition for Position {
259 #[inline]
260 fn position(&self) -> Position {
261 *self
262 }
263}
264
265impl HasPosition for u32 {
266 #[inline]
267 fn position(&self) -> Position {
268 Position::new(*self)
269 }
270}
271
272impl HasSpan for Span {
273 #[inline]
274 fn span(&self) -> Span {
275 *self
276 }
277}
278
279impl RangeBounds<u32> for Span {
280 #[inline]
281 fn start_bound(&self) -> Bound<&u32> {
282 Bound::Included(&self.start.offset)
283 }
284
285 #[inline]
286 fn end_bound(&self) -> Bound<&u32> {
287 Bound::Excluded(&self.end.offset)
288 }
289}
290
291impl<T: HasSpan> HasPosition for T {
294 #[inline]
295 fn position(&self) -> Position {
296 self.start_position()
297 }
298}
299
300impl HasFileId for Span {
301 #[inline]
302 fn file_id(&self) -> FileId {
303 self.file_id
304 }
305}
306
307impl<T: HasSpan> HasSpan for &T {
309 #[inline]
310 fn span(&self) -> Span {
311 (*self).span()
312 }
313}
314
315impl<T: HasSpan> HasSpan for Box<T> {
317 #[inline]
318 fn span(&self) -> Span {
319 self.as_ref().span()
320 }
321}
322
323impl From<Span> for Range<u32> {
324 #[inline]
325 fn from(span: Span) -> Range<u32> {
326 span.to_range()
327 }
328}
329
330impl From<&Span> for Range<u32> {
331 #[inline]
332 fn from(span: &Span) -> Range<u32> {
333 span.to_range()
334 }
335}
336
337impl From<Span> for Range<usize> {
338 #[inline]
339 fn from(span: Span) -> Range<usize> {
340 let start = span.start.offset as usize;
341 let end = span.end.offset as usize;
342
343 start..end
344 }
345}
346
347impl From<&Span> for Range<usize> {
348 #[inline]
349 fn from(span: &Span) -> Range<usize> {
350 let start = span.start.offset as usize;
351 let end = span.end.offset as usize;
352
353 start..end
354 }
355}
356
357impl From<Position> for u32 {
358 #[inline]
359 fn from(position: Position) -> u32 {
360 position.offset
361 }
362}
363
364impl From<&Position> for u32 {
365 #[inline]
366 fn from(position: &Position) -> u32 {
367 position.offset
368 }
369}
370
371impl From<u32> for Position {
372 #[inline]
373 fn from(offset: u32) -> Self {
374 Position { offset }
375 }
376}
377
378impl std::ops::Add<u32> for Position {
379 type Output = Position;
380
381 #[inline]
382 fn add(self, rhs: u32) -> Self::Output {
383 self.forward(rhs)
384 }
385}
386
387impl std::ops::Sub<u32> for Position {
388 type Output = Position;
389
390 #[inline]
391 fn sub(self, rhs: u32) -> Self::Output {
392 self.backward(rhs)
393 }
394}
395
396impl std::ops::AddAssign<u32> for Position {
397 #[inline]
398 fn add_assign(&mut self, rhs: u32) {
399 self.offset = self.offset.saturating_add(rhs);
400 }
401}
402
403impl std::ops::SubAssign<u32> for Position {
404 #[inline]
406 fn sub_assign(&mut self, rhs: u32) {
407 self.offset = self.offset.saturating_sub(rhs);
408 }
409}
410
411impl std::fmt::Display for Position {
412 #[inline]
413 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
414 write!(f, "{}", self.offset)
415 }
416}
417
418impl std::fmt::Display for Span {
419 #[inline]
420 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
421 write!(f, "{}..{}", self.start.offset, self.end.offset)
422 }
423}