1use std::ops::Range;
2
3use mago_source::HasSource;
4use serde::Deserialize;
5use serde::Serialize;
6
7use mago_source::SourceIdentifier;
8
9#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
10#[repr(C)]
11pub struct Position {
12 pub source: SourceIdentifier,
13 pub offset: usize,
14}
15
16#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
17#[repr(C)]
18pub struct Span {
19 pub start: Position,
20 pub end: Position,
21}
22
23pub trait HasPosition {
24 fn position(&self) -> Position;
25
26 #[inline]
27 fn offset(&self) -> usize {
28 self.position().offset
29 }
30}
31
32pub trait HasSpan {
33 fn span(&self) -> Span;
34
35 fn start_position(&self) -> Position {
36 self.span().start
37 }
38
39 fn end_position(&self) -> Position {
40 self.span().end
41 }
42}
43
44impl Position {
45 pub fn new(source: SourceIdentifier, offset: usize) -> Self {
46 Self { source, offset }
47 }
48
49 pub fn dummy(offset: usize) -> Self {
50 Self::new(SourceIdentifier::dummy(), offset)
51 }
52
53 pub fn start_of(source: SourceIdentifier) -> Self {
54 Self::new(source, 0)
55 }
56
57 pub const fn forward(&self, offset: usize) -> Self {
67 Self { source: self.source, offset: self.offset + offset }
68 }
69
70 pub fn backward(&self, offset: usize) -> Self {
80 Self { source: self.source, offset: self.offset - offset }
81 }
82
83 pub fn range_for(&self, length: usize) -> Range<usize> {
84 self.offset..self.offset + length
85 }
86}
87
88impl Span {
89 pub fn new(start: Position, end: Position) -> Self {
90 debug_assert!(start.source == end.source, "span start and end must be in the same file");
91
92 Self { start, end }
93 }
94
95 pub fn between(start: Span, end: Span) -> Self {
96 start.join(end)
97 }
98
99 pub fn join(self, other: Span) -> Span {
100 Span::new(self.start, other.end)
101 }
102
103 pub fn contains(&self, position: &impl HasPosition) -> bool {
104 self.has_offset(position.offset())
105 }
106
107 pub fn has_offset(&self, offset: usize) -> bool {
108 self.start.offset <= offset && offset <= self.end.offset
109 }
110
111 pub fn to_range(&self) -> Range<usize> {
112 self.start.offset..self.end.offset
113 }
114
115 pub fn length(&self) -> usize {
116 self.end.offset - self.start.offset
117 }
118
119 pub fn subspan(&self, start: usize, end: usize) -> Span {
120 Span::new(self.start.forward(start), self.start.forward(end))
121 }
122
123 pub fn is_before(&self, other: impl HasPosition) -> bool {
124 self.end.offset <= other.position().offset
125 }
126
127 pub fn is_after(&self, other: impl HasPosition) -> bool {
128 self.start.offset >= other.position().offset
129 }
130}
131
132impl HasPosition for Position {
133 fn position(&self) -> Position {
134 *self
135 }
136}
137
138impl HasSource for Position {
139 fn source(&self) -> SourceIdentifier {
140 self.source
141 }
142}
143
144impl HasSpan for Span {
145 fn span(&self) -> Span {
146 *self
147 }
148}
149
150impl<T> HasSpan for &T
151where
152 T: HasSpan,
153{
154 fn span(&self) -> Span {
155 (*self).span()
156 }
157}
158
159impl<T: HasSpan> HasPosition for T {
160 fn position(&self) -> Position {
161 self.start_position()
162 }
163}
164
165impl HasSource for Span {
166 fn source(&self) -> SourceIdentifier {
167 self.start.source
168 }
169}
170
171impl HasSource for dyn HasPosition {
172 fn source(&self) -> SourceIdentifier {
173 self.position().source()
174 }
175}
176
177impl HasSource for dyn HasSpan {
178 fn source(&self) -> SourceIdentifier {
179 self.span().source()
180 }
181}
182
183impl<T: HasSpan> HasSpan for Box<T> {
184 fn span(&self) -> Span {
185 self.as_ref().span()
186 }
187}
188
189impl From<Position> for usize {
190 fn from(position: Position) -> usize {
191 position.offset
192 }
193}
194
195impl From<&Position> for usize {
196 fn from(position: &Position) -> usize {
197 position.offset
198 }
199}
200
201impl From<Span> for Range<usize> {
202 fn from(span: Span) -> Range<usize> {
203 Range { start: span.start.into(), end: span.end.into() }
204 }
205}
206
207impl From<&Span> for Range<usize> {
208 fn from(span: &Span) -> Range<usize> {
209 Range { start: span.start.into(), end: span.end.into() }
210 }
211}
212
213impl std::fmt::Display for Position {
214 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
215 write!(f, "{}", self.offset)
216 }
217}
218
219impl std::fmt::Display for Span {
220 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
221 write!(f, "{}-{}", self.start, self.end)
222 }
223}
224
225impl From<Position> for SourceIdentifier {
226 fn from(position: Position) -> SourceIdentifier {
227 position.source
228 }
229}
230
231impl From<&Position> for SourceIdentifier {
232 fn from(position: &Position) -> SourceIdentifier {
233 position.source
234 }
235}
236
237impl From<Span> for SourceIdentifier {
238 fn from(span: Span) -> SourceIdentifier {
239 span.start.source
240 }
241}
242
243impl From<&Span> for SourceIdentifier {
244 fn from(span: &Span) -> SourceIdentifier {
245 span.start.source
246 }
247}