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 HasSpan for Span {
266 #[inline]
267 fn span(&self) -> Span {
268 *self
269 }
270}
271
272impl RangeBounds<u32> for Span {
273 #[inline]
274 fn start_bound(&self) -> Bound<&u32> {
275 Bound::Included(&self.start.offset)
276 }
277
278 #[inline]
279 fn end_bound(&self) -> Bound<&u32> {
280 Bound::Excluded(&self.end.offset)
281 }
282}
283
284impl<T: HasSpan> HasPosition for T {
287 #[inline]
288 fn position(&self) -> Position {
289 self.start_position()
290 }
291}
292
293impl HasFileId for Span {
294 #[inline]
295 fn file_id(&self) -> FileId {
296 self.file_id
297 }
298}
299
300impl<T: HasSpan> HasSpan for &T {
302 #[inline]
303 fn span(&self) -> Span {
304 (*self).span()
305 }
306}
307
308impl<T: HasSpan> HasSpan for Box<T> {
310 #[inline]
311 fn span(&self) -> Span {
312 self.as_ref().span()
313 }
314}
315
316impl From<Span> for Range<u32> {
317 #[inline]
318 fn from(span: Span) -> Range<u32> {
319 span.to_range()
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<usize> {
331 #[inline]
332 fn from(span: Span) -> Range<usize> {
333 let start = span.start.offset as usize;
334 let end = span.end.offset as usize;
335
336 start..end
337 }
338}
339
340impl From<&Span> for Range<usize> {
341 #[inline]
342 fn from(span: &Span) -> Range<usize> {
343 let start = span.start.offset as usize;
344 let end = span.end.offset as usize;
345
346 start..end
347 }
348}
349
350impl From<Position> for u32 {
351 #[inline]
352 fn from(position: Position) -> u32 {
353 position.offset
354 }
355}
356
357impl From<&Position> for u32 {
358 #[inline]
359 fn from(position: &Position) -> u32 {
360 position.offset
361 }
362}
363
364impl From<u32> for Position {
365 #[inline]
366 fn from(offset: u32) -> Self {
367 Position { offset }
368 }
369}
370
371impl std::ops::Add<u32> for Position {
372 type Output = Position;
373
374 #[inline]
375 fn add(self, rhs: u32) -> Self::Output {
376 self.forward(rhs)
377 }
378}
379
380impl std::ops::Sub<u32> for Position {
381 type Output = Position;
382
383 #[inline]
384 fn sub(self, rhs: u32) -> Self::Output {
385 self.backward(rhs)
386 }
387}
388
389impl std::ops::AddAssign<u32> for Position {
390 #[inline]
391 fn add_assign(&mut self, rhs: u32) {
392 self.offset = self.offset.saturating_add(rhs);
393 }
394}
395
396impl std::ops::SubAssign<u32> for Position {
397 #[inline]
399 fn sub_assign(&mut self, rhs: u32) {
400 self.offset = self.offset.saturating_sub(rhs);
401 }
402}
403
404impl std::fmt::Display for Position {
405 #[inline]
406 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
407 write!(f, "{}", self.offset)
408 }
409}
410
411impl std::fmt::Display for Span {
412 #[inline]
413 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
414 write!(f, "{}..{}", self.start.offset, self.end.offset)
415 }
416}