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 fn start_position(&self) -> Position {
58 self.span().start
59 }
60
61 fn start_offset(&self) -> u32 {
63 self.start_position().offset
64 }
65
66 fn end_position(&self) -> Position {
68 self.span().end
69 }
70
71 fn end_offset(&self) -> u32 {
73 self.end_position().offset
74 }
75}
76
77impl Position {
78 #[must_use]
80 pub const fn new(offset: u32) -> Self {
81 Self { offset }
82 }
83
84 #[must_use]
86 pub const fn zero() -> Self {
87 Self { offset: 0 }
88 }
89
90 #[must_use]
92 pub const fn is_zero(&self) -> bool {
93 self.offset == 0
94 }
95
96 #[must_use]
100 pub const fn forward(&self, offset: u32) -> Self {
101 Self { offset: self.offset.saturating_add(offset) }
102 }
103
104 #[must_use]
108 pub const fn backward(&self, offset: u32) -> Self {
109 Self { offset: self.offset.saturating_sub(offset) }
110 }
111
112 #[must_use]
114 pub const fn range_for(&self, length: u32) -> Range<u32> {
115 self.offset..self.offset.saturating_add(length)
116 }
117}
118
119impl Span {
120 #[must_use]
127 pub const fn new(file_id: FileId, start: Position, end: Position) -> Self {
128 Self { file_id, start, end }
129 }
130
131 #[must_use]
133 pub const fn zero() -> Self {
134 Self { file_id: FileId::zero(), start: Position::zero(), end: Position::zero() }
135 }
136
137 #[must_use]
139 pub fn dummy(start_offset: u32, end_offset: u32) -> Self {
140 Self::new(FileId::zero(), Position::new(start_offset), Position::new(end_offset))
141 }
142
143 #[must_use]
146 pub fn between(start: Span, end: Span) -> Self {
147 start.join(end)
148 }
149
150 #[must_use]
152 pub const fn is_zero(&self) -> bool {
153 self.start.is_zero() && self.end.is_zero()
154 }
155
156 #[must_use]
159 pub fn join(self, other: Span) -> Span {
160 Span::new(self.file_id, self.start, other.end)
161 }
162
163 #[must_use]
166 pub fn to_end(&self, end: Position) -> Span {
167 Span::new(self.file_id, self.start, end)
168 }
169
170 #[must_use]
173 pub fn from_start(&self, start: Position) -> Span {
174 Span::new(self.file_id, start, self.end)
175 }
176
177 #[must_use]
180 pub fn subspan(&self, start: u32, end: u32) -> Span {
181 Span::new(self.file_id, self.start.forward(start), self.start.forward(end))
182 }
183
184 pub fn contains(&self, position: &impl HasPosition) -> bool {
186 self.has_offset(position.offset())
187 }
188
189 #[must_use]
191 pub fn has_offset(&self, offset: u32) -> bool {
192 self.start.offset <= offset && offset <= self.end.offset
193 }
194
195 #[must_use]
197 pub fn to_range(&self) -> Range<u32> {
198 self.start.offset..self.end.offset
199 }
200
201 #[must_use]
203 pub fn to_range_usize(&self) -> Range<usize> {
204 let start = self.start.offset as usize;
205 let end = self.end.offset as usize;
206
207 start..end
208 }
209
210 #[must_use]
212 pub fn to_offset_tuple(&self) -> (u32, u32) {
213 (self.start.offset, self.end.offset)
214 }
215
216 #[must_use]
218 pub fn length(&self) -> u32 {
219 self.end.offset.saturating_sub(self.start.offset)
220 }
221
222 pub fn is_before(&self, other: &impl HasPosition) -> bool {
223 self.end.offset <= other.position().offset
224 }
225
226 pub fn is_after(&self, other: &impl HasPosition) -> bool {
227 self.start.offset >= other.position().offset
228 }
229}
230
231impl HasPosition for Position {
232 fn position(&self) -> Position {
233 *self
234 }
235}
236
237impl HasSpan for Span {
238 fn span(&self) -> Span {
239 *self
240 }
241}
242
243impl RangeBounds<u32> for Span {
244 fn start_bound(&self) -> Bound<&u32> {
245 Bound::Included(&self.start.offset)
246 }
247
248 fn end_bound(&self) -> Bound<&u32> {
249 Bound::Excluded(&self.end.offset)
250 }
251}
252
253impl<T: HasSpan> HasPosition for T {
256 fn position(&self) -> Position {
257 self.start_position()
258 }
259}
260
261impl HasFileId for Span {
262 fn file_id(&self) -> FileId {
263 self.file_id
264 }
265}
266
267impl<T: HasSpan> HasSpan for &T {
269 fn span(&self) -> Span {
270 (*self).span()
271 }
272}
273
274impl<T: HasSpan> HasSpan for Box<T> {
276 fn span(&self) -> Span {
277 self.as_ref().span()
278 }
279}
280
281impl From<Span> for Range<u32> {
282 fn from(span: Span) -> Range<u32> {
283 span.to_range()
284 }
285}
286
287impl From<&Span> for Range<u32> {
288 fn from(span: &Span) -> Range<u32> {
289 span.to_range()
290 }
291}
292
293impl From<Span> for Range<usize> {
294 fn from(span: Span) -> Range<usize> {
295 let start = span.start.offset as usize;
296 let end = span.end.offset as usize;
297
298 start..end
299 }
300}
301
302impl From<&Span> for Range<usize> {
303 fn from(span: &Span) -> Range<usize> {
304 let start = span.start.offset as usize;
305 let end = span.end.offset as usize;
306
307 start..end
308 }
309}
310
311impl From<Position> for u32 {
312 fn from(position: Position) -> u32 {
313 position.offset
314 }
315}
316
317impl From<&Position> for u32 {
318 fn from(position: &Position) -> u32 {
319 position.offset
320 }
321}
322
323impl From<u32> for Position {
324 fn from(offset: u32) -> Self {
325 Position { offset }
326 }
327}
328
329impl std::ops::Add<u32> for Position {
330 type Output = Position;
331
332 fn add(self, rhs: u32) -> Self::Output {
333 self.forward(rhs)
334 }
335}
336
337impl std::ops::Sub<u32> for Position {
338 type Output = Position;
339
340 fn sub(self, rhs: u32) -> Self::Output {
341 self.backward(rhs)
342 }
343}
344
345impl std::ops::AddAssign<u32> for Position {
346 fn add_assign(&mut self, rhs: u32) {
347 self.offset = self.offset.saturating_add(rhs);
348 }
349}
350
351impl std::ops::SubAssign<u32> for Position {
352 fn sub_assign(&mut self, rhs: u32) {
354 self.offset = self.offset.saturating_sub(rhs);
355 }
356}
357
358impl std::fmt::Display for Position {
359 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
360 write!(f, "{}", self.offset)
361 }
362}
363
364impl std::fmt::Display for Span {
365 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
366 write!(f, "{}..{}", self.start.offset, self.end.offset)
367 }
368}