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 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}