mago_span/
lib.rs

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    /// Return the position moved by the given offset.
58    ///
59    /// # Parameters
60    ///
61    /// - `offset`: The offset to move the position by.
62    ///
63    /// # Returns
64    ///
65    /// The position moved by the given offset.
66    pub const fn forward(&self, offset: usize) -> Self {
67        Self { source: self.source, offset: self.offset.saturating_add(offset) }
68    }
69
70    /// Return the position moved back by the given offset.
71    ///
72    /// # Parameters
73    ///
74    /// - `offset`: The offset to move the position back by.
75    ///
76    /// # Returns
77    ///
78    /// The position moved back by the given offset.
79    pub fn backward(&self, offset: usize) -> Self {
80        Self { source: self.source, offset: self.offset.saturating_sub(offset) }
81    }
82
83    pub fn range_for(&self, length: usize) -> Range<usize> {
84        self.offset..self.offset.saturating_add(length)
85    }
86}
87
88impl Span {
89    pub fn new(start: Position, end: Position) -> Self {
90        debug_assert!(
91            start.source.0.is_empty() || end.source.0.is_empty() || start.source == end.source,
92            "span start and end must be in the same file",
93        );
94
95        Self { start, end }
96    }
97
98    pub fn dummy(start_offset: usize, end_offset: usize) -> Self {
99        Self::new(Position::dummy(start_offset), Position::dummy(end_offset))
100    }
101
102    pub fn between(start: Span, end: Span) -> Self {
103        start.join(end)
104    }
105
106    pub fn join(self, other: Span) -> Span {
107        Span::new(self.start, other.end)
108    }
109
110    pub fn contains(&self, position: &impl HasPosition) -> bool {
111        self.has_offset(position.offset())
112    }
113
114    pub fn has_offset(&self, offset: usize) -> bool {
115        self.start.offset <= offset && offset <= self.end.offset
116    }
117
118    pub fn to_range(&self) -> Range<usize> {
119        self.start.offset..self.end.offset
120    }
121
122    pub fn to_tuple(&self) -> (usize, usize) {
123        (self.start.offset, self.end.offset)
124    }
125
126    pub fn length(&self) -> usize {
127        self.end.offset - self.start.offset
128    }
129
130    pub fn with_start(&self, start: Position) -> Span {
131        Span::new(start, self.end)
132    }
133
134    pub fn with_end(&self, end: Position) -> Span {
135        Span::new(self.start, end)
136    }
137
138    pub fn subspan(&self, start: usize, end: usize) -> Span {
139        Span::new(self.start.forward(start), self.start.forward(end))
140    }
141
142    pub fn is_before(&self, other: impl HasPosition) -> bool {
143        self.end.offset <= other.position().offset
144    }
145
146    pub fn is_after(&self, other: impl HasPosition) -> bool {
147        self.start.offset >= other.position().offset
148    }
149}
150
151impl HasPosition for Position {
152    fn position(&self) -> Position {
153        *self
154    }
155}
156
157impl HasSource for Position {
158    fn source(&self) -> SourceIdentifier {
159        self.source
160    }
161}
162
163impl HasSpan for Span {
164    fn span(&self) -> Span {
165        *self
166    }
167}
168
169impl<T> HasSpan for &T
170where
171    T: HasSpan,
172{
173    fn span(&self) -> Span {
174        (*self).span()
175    }
176}
177
178impl<T: HasSpan> HasPosition for T {
179    fn position(&self) -> Position {
180        self.start_position()
181    }
182}
183
184impl HasSource for Span {
185    fn source(&self) -> SourceIdentifier {
186        self.start.source
187    }
188}
189
190impl HasSource for dyn HasPosition {
191    fn source(&self) -> SourceIdentifier {
192        self.position().source()
193    }
194}
195
196impl HasSource for dyn HasSpan {
197    fn source(&self) -> SourceIdentifier {
198        self.span().source()
199    }
200}
201
202impl<T: HasSpan> HasSpan for Box<T> {
203    fn span(&self) -> Span {
204        self.as_ref().span()
205    }
206}
207
208impl From<Position> for usize {
209    fn from(position: Position) -> usize {
210        position.offset
211    }
212}
213
214impl From<&Position> for usize {
215    fn from(position: &Position) -> usize {
216        position.offset
217    }
218}
219
220impl From<Span> for Range<usize> {
221    fn from(span: Span) -> Range<usize> {
222        Range { start: span.start.into(), end: span.end.into() }
223    }
224}
225
226impl From<&Span> for Range<usize> {
227    fn from(span: &Span) -> Range<usize> {
228        Range { start: span.start.into(), end: span.end.into() }
229    }
230}
231
232impl std::fmt::Display for Position {
233    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234        write!(f, "{}", self.offset)
235    }
236}
237
238impl std::fmt::Display for Span {
239    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
240        write!(f, "{}-{}", self.start, self.end)
241    }
242}
243
244impl From<Position> for SourceIdentifier {
245    fn from(position: Position) -> SourceIdentifier {
246        position.source
247    }
248}
249
250impl From<&Position> for SourceIdentifier {
251    fn from(position: &Position) -> SourceIdentifier {
252        position.source
253    }
254}
255
256impl From<Span> for SourceIdentifier {
257    fn from(span: Span) -> SourceIdentifier {
258        span.start.source
259    }
260}
261
262impl From<&Span> for SourceIdentifier {
263    fn from(span: &Span) -> SourceIdentifier {
264        span.start.source
265    }
266}
267
268impl std::ops::Add<usize> for Position {
269    type Output = Position;
270
271    fn add(self, rhs: usize) -> Self::Output {
272        self.forward(rhs)
273    }
274}
275
276impl std::ops::Sub<usize> for Position {
277    type Output = Position;
278
279    fn sub(self, rhs: usize) -> Self::Output {
280        self.backward(rhs)
281    }
282}
283
284impl std::ops::AddAssign<usize> for Position {
285    fn add_assign(&mut self, rhs: usize) {
286        self.offset += rhs;
287    }
288}
289
290impl std::ops::SubAssign<usize> for Position {
291    fn sub_assign(&mut self, rhs: usize) {
292        self.offset -= rhs;
293    }
294}