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 end_position(&self) -> Position {
61 self.span().end
62 }
63}
64
65impl Position {
66 pub const fn new(offset: u32) -> Self {
68 Self { offset }
69 }
70
71 pub const fn zero() -> Self {
73 Self { offset: 0 }
74 }
75
76 pub const fn is_zero(&self) -> bool {
78 self.offset == 0
79 }
80
81 pub const fn forward(&self, offset: u32) -> Self {
85 Self { offset: self.offset.saturating_add(offset) }
86 }
87
88 pub const fn backward(&self, offset: u32) -> Self {
92 Self { offset: self.offset.saturating_sub(offset) }
93 }
94
95 pub const fn range_for(&self, length: u32) -> Range<u32> {
97 self.offset..self.offset.saturating_add(length)
98 }
99}
100
101impl Span {
102 pub const fn new(file_id: FileId, start: Position, end: Position) -> Self {
109 Self { file_id, start, end }
110 }
111
112 pub const fn zero() -> Self {
114 Self { file_id: FileId::zero(), start: Position::zero(), end: Position::zero() }
115 }
116
117 pub fn dummy(start_offset: u32, end_offset: u32) -> Self {
119 Self::new(FileId::zero(), Position::new(start_offset), Position::new(end_offset))
120 }
121
122 pub fn between(start: Span, end: Span) -> Self {
125 start.join(end)
126 }
127
128 pub const fn is_zero(&self) -> bool {
130 self.start.is_zero() && self.end.is_zero()
131 }
132
133 pub fn join(self, other: Span) -> Span {
136 Span::new(self.file_id, self.start, other.end)
137 }
138
139 pub fn to_end(&self, end: Position) -> Span {
142 Span::new(self.file_id, self.start, end)
143 }
144
145 pub fn from_start(&self, start: Position) -> Span {
148 Span::new(self.file_id, start, self.end)
149 }
150
151 pub fn subspan(&self, start: u32, end: u32) -> Span {
154 Span::new(self.file_id, self.start.forward(start), self.start.forward(end))
155 }
156
157 pub fn contains(&self, position: &impl HasPosition) -> bool {
159 self.has_offset(position.offset())
160 }
161
162 pub fn has_offset(&self, offset: u32) -> bool {
164 self.start.offset <= offset && offset <= self.end.offset
165 }
166
167 pub fn to_range(&self) -> Range<u32> {
169 self.start.offset..self.end.offset
170 }
171
172 pub fn to_range_usize(&self) -> Range<usize> {
174 let start = self.start.offset as usize;
175 let end = self.end.offset as usize;
176
177 start..end
178 }
179
180 pub fn to_offset_tuple(&self) -> (u32, u32) {
182 (self.start.offset, self.end.offset)
183 }
184
185 pub fn length(&self) -> u32 {
187 self.end.offset.saturating_sub(self.start.offset)
188 }
189
190 pub fn is_before(&self, other: impl HasPosition) -> bool {
191 self.end.offset <= other.position().offset
192 }
193
194 pub fn is_after(&self, other: impl HasPosition) -> bool {
195 self.start.offset >= other.position().offset
196 }
197}
198
199impl HasPosition for Position {
200 fn position(&self) -> Position {
201 *self
202 }
203}
204
205impl HasSpan for Span {
206 fn span(&self) -> Span {
207 *self
208 }
209}
210
211impl<T: HasSpan> HasPosition for T {
214 fn position(&self) -> Position {
215 self.start_position()
216 }
217}
218
219impl HasFileId for Span {
220 fn file_id(&self) -> FileId {
221 self.file_id
222 }
223}
224
225impl<T: HasSpan> HasSpan for &T {
227 fn span(&self) -> Span {
228 (*self).span()
229 }
230}
231
232impl<T: HasSpan> HasSpan for Box<T> {
234 fn span(&self) -> Span {
235 self.as_ref().span()
236 }
237}
238
239impl From<Span> for Range<u32> {
240 fn from(span: Span) -> Range<u32> {
241 span.to_range()
242 }
243}
244
245impl From<&Span> for Range<u32> {
246 fn from(span: &Span) -> Range<u32> {
247 span.to_range()
248 }
249}
250
251impl From<Span> for Range<usize> {
252 fn from(span: Span) -> Range<usize> {
253 let start = span.start.offset as usize;
254 let end = span.end.offset as usize;
255
256 start..end
257 }
258}
259
260impl From<&Span> for Range<usize> {
261 fn from(span: &Span) -> Range<usize> {
262 let start = span.start.offset as usize;
263 let end = span.end.offset as usize;
264
265 start..end
266 }
267}
268
269impl From<Position> for u32 {
270 fn from(position: Position) -> u32 {
271 position.offset
272 }
273}
274
275impl From<&Position> for u32 {
276 fn from(position: &Position) -> u32 {
277 position.offset
278 }
279}
280
281impl From<u32> for Position {
282 fn from(offset: u32) -> Self {
283 Position { offset }
284 }
285}
286
287impl std::ops::Add<u32> for Position {
288 type Output = Position;
289
290 fn add(self, rhs: u32) -> Self::Output {
291 self.forward(rhs)
292 }
293}
294
295impl std::ops::Sub<u32> for Position {
296 type Output = Position;
297
298 fn sub(self, rhs: u32) -> Self::Output {
299 self.backward(rhs)
300 }
301}
302
303impl std::ops::AddAssign<u32> for Position {
304 fn add_assign(&mut self, rhs: u32) {
305 self.offset = self.offset.saturating_add(rhs);
306 }
307}
308
309impl std::ops::SubAssign<u32> for Position {
310 fn sub_assign(&mut self, rhs: u32) {
312 self.offset = self.offset.saturating_sub(rhs);
313 }
314}
315
316impl std::fmt::Display for Position {
317 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
318 write!(f, "{}", self.offset)
319 }
320}
321
322impl std::fmt::Display for Span {
323 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
324 write!(f, "{}..{}", self.start.offset, self.end.offset)
325 }
326}