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 fn new(file_id: FileId, start: Position, end: Position) -> Self {
109 Self { file_id, start, end }
110 }
111
112 pub fn dummy(start_offset: u32, end_offset: u32) -> Self {
114 Self::new(FileId::zero(), Position::new(start_offset), Position::new(end_offset))
115 }
116
117 pub fn between(start: Span, end: Span) -> Self {
120 start.join(end)
121 }
122
123 pub fn join(self, other: Span) -> Span {
126 Span::new(self.file_id, self.start, other.end)
127 }
128
129 pub fn to_end(&self, end: Position) -> Span {
132 Span::new(self.file_id, self.start, end)
133 }
134
135 pub fn from_start(&self, start: Position) -> Span {
138 Span::new(self.file_id, start, self.end)
139 }
140
141 pub fn subspan(&self, start: u32, end: u32) -> Span {
144 Span::new(self.file_id, self.start.forward(start), self.start.forward(end))
145 }
146
147 pub fn contains(&self, position: &impl HasPosition) -> bool {
149 self.has_offset(position.offset())
150 }
151
152 pub fn has_offset(&self, offset: u32) -> bool {
154 self.start.offset <= offset && offset <= self.end.offset
155 }
156
157 pub fn to_range(&self) -> Range<u32> {
159 self.start.offset..self.end.offset
160 }
161
162 pub fn to_range_usize(&self) -> Range<usize> {
164 let start = self.start.offset as usize;
165 let end = self.end.offset as usize;
166
167 start..end
168 }
169
170 pub fn to_offset_tuple(&self) -> (u32, u32) {
172 (self.start.offset, self.end.offset)
173 }
174
175 pub fn length(&self) -> u32 {
177 self.end.offset.saturating_sub(self.start.offset)
178 }
179
180 pub fn is_before(&self, other: impl HasPosition) -> bool {
181 self.end.offset <= other.position().offset
182 }
183
184 pub fn is_after(&self, other: impl HasPosition) -> bool {
185 self.start.offset >= other.position().offset
186 }
187}
188
189impl HasPosition for Position {
190 fn position(&self) -> Position {
191 *self
192 }
193}
194
195impl HasSpan for Span {
196 fn span(&self) -> Span {
197 *self
198 }
199}
200
201impl<T: HasSpan> HasPosition for T {
204 fn position(&self) -> Position {
205 self.start_position()
206 }
207}
208
209impl HasFileId for Span {
210 fn file_id(&self) -> FileId {
211 self.file_id
212 }
213}
214
215impl<T: HasSpan> HasSpan for &T {
217 fn span(&self) -> Span {
218 (*self).span()
219 }
220}
221
222impl<T: HasSpan> HasSpan for Box<T> {
224 fn span(&self) -> Span {
225 self.as_ref().span()
226 }
227}
228
229impl From<Span> for Range<u32> {
230 fn from(span: Span) -> Range<u32> {
231 span.to_range()
232 }
233}
234
235impl From<&Span> for Range<u32> {
236 fn from(span: &Span) -> Range<u32> {
237 span.to_range()
238 }
239}
240
241impl From<Span> for Range<usize> {
242 fn from(span: Span) -> Range<usize> {
243 let start = span.start.offset as usize;
244 let end = span.end.offset as usize;
245
246 start..end
247 }
248}
249
250impl From<&Span> for Range<usize> {
251 fn from(span: &Span) -> Range<usize> {
252 let start = span.start.offset as usize;
253 let end = span.end.offset as usize;
254
255 start..end
256 }
257}
258
259impl From<Position> for u32 {
260 fn from(position: Position) -> u32 {
261 position.offset
262 }
263}
264
265impl From<&Position> for u32 {
266 fn from(position: &Position) -> u32 {
267 position.offset
268 }
269}
270
271impl From<u32> for Position {
272 fn from(offset: u32) -> Self {
273 Position { offset }
274 }
275}
276
277impl std::ops::Add<u32> for Position {
278 type Output = Position;
279
280 fn add(self, rhs: u32) -> Self::Output {
281 self.forward(rhs)
282 }
283}
284
285impl std::ops::Sub<u32> for Position {
286 type Output = Position;
287
288 fn sub(self, rhs: u32) -> Self::Output {
289 self.backward(rhs)
290 }
291}
292
293impl std::ops::AddAssign<u32> for Position {
294 fn add_assign(&mut self, rhs: u32) {
295 self.offset = self.offset.saturating_add(rhs);
296 }
297}
298
299impl std::ops::SubAssign<u32> for Position {
300 fn sub_assign(&mut self, rhs: u32) {
302 self.offset = self.offset.saturating_sub(rhs);
303 }
304}
305
306impl std::fmt::Display for Position {
307 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
308 write!(f, "{}", self.offset)
309 }
310}
311
312impl std::fmt::Display for Span {
313 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
314 write!(f, "{}..{}", self.start.offset, self.end.offset)
315 }
316}