1use std::ops::Range;
9
10use serde::Deserialize;
11use serde::Serialize;
12
13use mago_database::file::FileId;
14use mago_database::file::HasFileId;
15
16#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
18#[repr(transparent)]
19pub struct Position {
20 pub offset: u32,
21}
22
23#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
28pub struct Span {
29 pub file_id: FileId,
31 pub start: Position,
33 pub end: Position,
35}
36
37pub trait HasPosition {
39 fn position(&self) -> Position;
41
42 #[inline]
44 fn offset(&self) -> u32 {
45 self.position().offset
46 }
47}
48
49pub trait HasSpan {
51 fn span(&self) -> Span;
53
54 fn start_position(&self) -> Position {
56 self.span().start
57 }
58
59 fn start_offset(&self) -> u32 {
61 self.start_position().offset
62 }
63
64 fn end_position(&self) -> Position {
66 self.span().end
67 }
68
69 fn end_offset(&self) -> u32 {
71 self.end_position().offset
72 }
73}
74
75impl Position {
76 pub const fn new(offset: u32) -> Self {
78 Self { offset }
79 }
80
81 pub const fn zero() -> Self {
83 Self { offset: 0 }
84 }
85
86 pub const fn is_zero(&self) -> bool {
88 self.offset == 0
89 }
90
91 pub const fn forward(&self, offset: u32) -> Self {
95 Self { offset: self.offset.saturating_add(offset) }
96 }
97
98 pub const fn backward(&self, offset: u32) -> Self {
102 Self { offset: self.offset.saturating_sub(offset) }
103 }
104
105 pub const fn range_for(&self, length: u32) -> Range<u32> {
107 self.offset..self.offset.saturating_add(length)
108 }
109}
110
111impl Span {
112 pub const fn new(file_id: FileId, start: Position, end: Position) -> Self {
119 Self { file_id, start, end }
120 }
121
122 pub const fn zero() -> Self {
124 Self { file_id: FileId::zero(), start: Position::zero(), end: Position::zero() }
125 }
126
127 pub fn dummy(start_offset: u32, end_offset: u32) -> Self {
129 Self::new(FileId::zero(), Position::new(start_offset), Position::new(end_offset))
130 }
131
132 pub fn between(start: Span, end: Span) -> Self {
135 start.join(end)
136 }
137
138 pub const fn is_zero(&self) -> bool {
140 self.start.is_zero() && self.end.is_zero()
141 }
142
143 pub fn join(self, other: Span) -> Span {
146 Span::new(self.file_id, self.start, other.end)
147 }
148
149 pub fn to_end(&self, end: Position) -> Span {
152 Span::new(self.file_id, self.start, end)
153 }
154
155 pub fn from_start(&self, start: Position) -> Span {
158 Span::new(self.file_id, start, self.end)
159 }
160
161 pub fn subspan(&self, start: u32, end: u32) -> Span {
164 Span::new(self.file_id, self.start.forward(start), self.start.forward(end))
165 }
166
167 pub fn contains(&self, position: &impl HasPosition) -> bool {
169 self.has_offset(position.offset())
170 }
171
172 pub fn has_offset(&self, offset: u32) -> bool {
174 self.start.offset <= offset && offset <= self.end.offset
175 }
176
177 pub fn to_range(&self) -> Range<u32> {
179 self.start.offset..self.end.offset
180 }
181
182 pub fn to_range_usize(&self) -> Range<usize> {
184 let start = self.start.offset as usize;
185 let end = self.end.offset as usize;
186
187 start..end
188 }
189
190 pub fn to_offset_tuple(&self) -> (u32, u32) {
192 (self.start.offset, self.end.offset)
193 }
194
195 pub fn length(&self) -> u32 {
197 self.end.offset.saturating_sub(self.start.offset)
198 }
199
200 pub fn is_before(&self, other: impl HasPosition) -> bool {
201 self.end.offset <= other.position().offset
202 }
203
204 pub fn is_after(&self, other: impl HasPosition) -> bool {
205 self.start.offset >= other.position().offset
206 }
207}
208
209impl HasPosition for Position {
210 fn position(&self) -> Position {
211 *self
212 }
213}
214
215impl HasSpan for Span {
216 fn span(&self) -> Span {
217 *self
218 }
219}
220
221impl<T: HasSpan> HasPosition for T {
224 fn position(&self) -> Position {
225 self.start_position()
226 }
227}
228
229impl HasFileId for Span {
230 fn file_id(&self) -> FileId {
231 self.file_id
232 }
233}
234
235impl<T: HasSpan> HasSpan for &T {
237 fn span(&self) -> Span {
238 (*self).span()
239 }
240}
241
242impl<T: HasSpan> HasSpan for Box<T> {
244 fn span(&self) -> Span {
245 self.as_ref().span()
246 }
247}
248
249impl From<Span> for Range<u32> {
250 fn from(span: Span) -> Range<u32> {
251 span.to_range()
252 }
253}
254
255impl From<&Span> for Range<u32> {
256 fn from(span: &Span) -> Range<u32> {
257 span.to_range()
258 }
259}
260
261impl From<Span> for Range<usize> {
262 fn from(span: Span) -> Range<usize> {
263 let start = span.start.offset as usize;
264 let end = span.end.offset as usize;
265
266 start..end
267 }
268}
269
270impl From<&Span> for Range<usize> {
271 fn from(span: &Span) -> Range<usize> {
272 let start = span.start.offset as usize;
273 let end = span.end.offset as usize;
274
275 start..end
276 }
277}
278
279impl From<Position> for u32 {
280 fn from(position: Position) -> u32 {
281 position.offset
282 }
283}
284
285impl From<&Position> for u32 {
286 fn from(position: &Position) -> u32 {
287 position.offset
288 }
289}
290
291impl From<u32> for Position {
292 fn from(offset: u32) -> Self {
293 Position { offset }
294 }
295}
296
297impl std::ops::Add<u32> for Position {
298 type Output = Position;
299
300 fn add(self, rhs: u32) -> Self::Output {
301 self.forward(rhs)
302 }
303}
304
305impl std::ops::Sub<u32> for Position {
306 type Output = Position;
307
308 fn sub(self, rhs: u32) -> Self::Output {
309 self.backward(rhs)
310 }
311}
312
313impl std::ops::AddAssign<u32> for Position {
314 fn add_assign(&mut self, rhs: u32) {
315 self.offset = self.offset.saturating_add(rhs);
316 }
317}
318
319impl std::ops::SubAssign<u32> for Position {
320 fn sub_assign(&mut self, rhs: u32) {
322 self.offset = self.offset.saturating_sub(rhs);
323 }
324}
325
326impl std::fmt::Display for Position {
327 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
328 write!(f, "{}", self.offset)
329 }
330}
331
332impl std::fmt::Display for Span {
333 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
334 write!(f, "{}..{}", self.start.offset, self.end.offset)
335 }
336}