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)]
24#[repr(C)]
25pub struct Position {
26 pub file_id: FileId,
28 pub offset: usize,
30}
31
32#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
39#[repr(C)]
40pub struct Span {
41 pub start: Position,
43 pub end: Position,
45}
46
47pub trait HasPosition {
49 fn position(&self) -> Position;
51
52 #[inline]
54 fn offset(&self) -> usize {
55 self.position().offset
56 }
57}
58
59pub trait HasSpan {
61 fn span(&self) -> Span;
63
64 fn start_position(&self) -> Position {
66 self.span().start
67 }
68
69 fn end_position(&self) -> Position {
71 self.span().end
72 }
73}
74
75impl Position {
76 pub fn new(file_id: FileId, offset: usize) -> Self {
78 Self { file_id, offset }
79 }
80
81 pub fn dummy(offset: usize) -> Self {
85 Self::new(FileId::zero(), offset)
86 }
87
88 pub fn start_of(file_id: FileId) -> Self {
90 Self::new(file_id, 0)
91 }
92
93 pub const fn forward(&self, offset: usize) -> Self {
97 Self { file_id: self.file_id, offset: self.offset.saturating_add(offset) }
98 }
99
100 pub fn backward(&self, offset: usize) -> Self {
104 Self { file_id: self.file_id, offset: self.offset.saturating_sub(offset) }
105 }
106
107 pub fn range_for(&self, length: usize) -> Range<usize> {
109 self.offset..self.offset.saturating_add(length)
110 }
111}
112
113impl Span {
114 pub fn new(start: Position, end: Position) -> Self {
121 debug_assert!(
122 start.file_id.is_zero() || end.file_id.is_zero() || start.file_id == end.file_id,
123 "span start and end must be in the same file",
124 );
125 Self { start, end }
126 }
127
128 pub fn dummy(start_offset: usize, end_offset: usize) -> Self {
130 Self::new(Position::dummy(start_offset), Position::dummy(end_offset))
131 }
132
133 pub fn between(start: Span, end: Span) -> Self {
136 start.join(end)
137 }
138
139 pub fn join(self, other: Span) -> Span {
142 Span::new(self.start, other.end)
143 }
144
145 pub fn to_end(&self, end: Position) -> Span {
148 Span::new(self.start, end)
149 }
150
151 pub fn from_start(&self, start: Position) -> Span {
154 Span::new(start, self.end)
155 }
156
157 pub fn subspan(&self, start: usize, end: usize) -> Span {
160 Span::new(self.start.forward(start), self.start.forward(end))
161 }
162
163 pub fn contains(&self, position: &impl HasPosition) -> bool {
165 self.has_offset(position.offset())
166 }
167
168 pub fn has_offset(&self, offset: usize) -> bool {
170 self.start.offset <= offset && offset <= self.end.offset
171 }
172
173 pub fn to_range(&self) -> Range<usize> {
175 self.start.offset..self.end.offset
176 }
177
178 pub fn to_offset_tuple(&self) -> (usize, usize) {
180 (self.start.offset, self.end.offset)
181 }
182
183 pub fn length(&self) -> usize {
185 self.end.offset.saturating_sub(self.start.offset)
186 }
187
188 pub fn is_before(&self, other: impl HasPosition) -> bool {
189 self.end.offset <= other.position().offset
190 }
191
192 pub fn is_after(&self, other: impl HasPosition) -> bool {
193 self.start.offset >= other.position().offset
194 }
195}
196
197impl HasPosition for Position {
198 fn position(&self) -> Position {
199 *self
200 }
201}
202
203impl HasSpan for Span {
204 fn span(&self) -> Span {
205 *self
206 }
207}
208
209impl<T: HasSpan> HasPosition for T {
212 fn position(&self) -> Position {
213 self.start_position()
214 }
215}
216
217impl HasFileId for Position {
218 fn file_id(&self) -> FileId {
219 self.file_id
220 }
221}
222
223impl HasFileId for Span {
224 fn file_id(&self) -> FileId {
225 self.start.file_id
226 }
227}
228
229impl<T: HasSpan> HasSpan for &T {
231 fn span(&self) -> Span {
232 (*self).span()
233 }
234}
235
236impl<T: HasSpan> HasSpan for Box<T> {
238 fn span(&self) -> Span {
239 self.as_ref().span()
240 }
241}
242
243impl From<Span> for Range<usize> {
244 fn from(span: Span) -> Range<usize> {
245 span.to_range()
246 }
247}
248
249impl From<&Span> for Range<usize> {
250 fn from(span: &Span) -> Range<usize> {
251 span.to_range()
252 }
253}
254
255impl From<Position> for usize {
256 fn from(position: Position) -> usize {
257 position.offset
258 }
259}
260
261impl From<&Position> for usize {
262 fn from(position: &Position) -> usize {
263 position.offset
264 }
265}
266
267impl std::ops::Add<usize> for Position {
268 type Output = Position;
269
270 fn add(self, rhs: usize) -> Self::Output {
271 self.forward(rhs)
272 }
273}
274
275impl std::ops::Sub<usize> for Position {
276 type Output = Position;
277
278 fn sub(self, rhs: usize) -> Self::Output {
279 self.backward(rhs)
280 }
281}
282
283impl std::ops::AddAssign<usize> for Position {
284 fn add_assign(&mut self, rhs: usize) {
285 self.offset = self.offset.saturating_add(rhs);
286 }
287}
288
289impl std::ops::SubAssign<usize> for Position {
290 fn sub_assign(&mut self, rhs: usize) {
292 self.offset = self.offset.saturating_sub(rhs);
293 }
294}
295
296impl std::fmt::Display for Position {
297 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
298 write!(f, "{}", self.offset)
299 }
300}
301
302impl std::fmt::Display for Span {
303 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
304 write!(f, "{}..{}", self.start.offset, self.end.offset)
305 }
306}