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