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