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 + 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 - 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 to_tuple(&self) -> (usize, usize) {
116        (self.start.offset, self.end.offset)
117    }
118
119    pub fn length(&self) -> usize {
120        self.end.offset - self.start.offset
121    }
122
123    pub fn with_start(&self, start: Position) -> Span {
124        Span::new(start, self.end)
125    }
126
127    pub fn with_end(&self, end: Position) -> Span {
128        Span::new(self.start, end)
129    }
130
131    pub fn subspan(&self, start: usize, end: usize) -> Span {
132        Span::new(self.start.forward(start), self.start.forward(end))
133    }
134
135    pub fn is_before(&self, other: impl HasPosition) -> bool {
136        self.end.offset <= other.position().offset
137    }
138
139    pub fn is_after(&self, other: impl HasPosition) -> bool {
140        self.start.offset >= other.position().offset
141    }
142}
143
144impl HasPosition for Position {
145    fn position(&self) -> Position {
146        *self
147    }
148}
149
150impl HasSource for Position {
151    fn source(&self) -> SourceIdentifier {
152        self.source
153    }
154}
155
156impl HasSpan for Span {
157    fn span(&self) -> Span {
158        *self
159    }
160}
161
162impl<T> HasSpan for &T
163where
164    T: HasSpan,
165{
166    fn span(&self) -> Span {
167        (*self).span()
168    }
169}
170
171impl<T: HasSpan> HasPosition for T {
172    fn position(&self) -> Position {
173        self.start_position()
174    }
175}
176
177impl HasSource for Span {
178    fn source(&self) -> SourceIdentifier {
179        self.start.source
180    }
181}
182
183impl HasSource for dyn HasPosition {
184    fn source(&self) -> SourceIdentifier {
185        self.position().source()
186    }
187}
188
189impl HasSource for dyn HasSpan {
190    fn source(&self) -> SourceIdentifier {
191        self.span().source()
192    }
193}
194
195impl<T: HasSpan> HasSpan for Box<T> {
196    fn span(&self) -> Span {
197        self.as_ref().span()
198    }
199}
200
201impl From<Position> for usize {
202    fn from(position: Position) -> usize {
203        position.offset
204    }
205}
206
207impl From<&Position> for usize {
208    fn from(position: &Position) -> usize {
209        position.offset
210    }
211}
212
213impl From<Span> for Range<usize> {
214    fn from(span: Span) -> Range<usize> {
215        Range { start: span.start.into(), end: span.end.into() }
216    }
217}
218
219impl From<&Span> for Range<usize> {
220    fn from(span: &Span) -> Range<usize> {
221        Range { start: span.start.into(), end: span.end.into() }
222    }
223}
224
225impl std::fmt::Display for Position {
226    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
227        write!(f, "{}", self.offset)
228    }
229}
230
231impl std::fmt::Display for Span {
232    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
233        write!(f, "{}-{}", self.start, self.end)
234    }
235}
236
237impl From<Position> for SourceIdentifier {
238    fn from(position: Position) -> SourceIdentifier {
239        position.source
240    }
241}
242
243impl From<&Position> for SourceIdentifier {
244    fn from(position: &Position) -> SourceIdentifier {
245        position.source
246    }
247}
248
249impl From<Span> for SourceIdentifier {
250    fn from(span: Span) -> SourceIdentifier {
251        span.start.source
252    }
253}
254
255impl From<&Span> for SourceIdentifier {
256    fn from(span: &Span) -> SourceIdentifier {
257        span.start.source
258    }
259}
260
261impl std::ops::Add<usize> for Position {
262    type Output = Position;
263
264    fn add(self, rhs: usize) -> Self::Output {
265        self.forward(rhs)
266    }
267}
268
269impl std::ops::Sub<usize> for Position {
270    type Output = Position;
271
272    fn sub(self, rhs: usize) -> Self::Output {
273        self.backward(rhs)
274    }
275}
276
277impl std::ops::AddAssign<usize> for Position {
278    fn add_assign(&mut self, rhs: usize) {
279        self.offset += rhs;
280    }
281}
282
283impl std::ops::SubAssign<usize> for Position {
284    fn sub_assign(&mut self, rhs: usize) {
285        self.offset -= rhs;
286    }
287}