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    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    /// Return the position moved by the given offset.
57    ///
58    /// # Parameters
59    ///
60    /// - `offset`: The offset to move the position by.
61    ///
62    /// # Returns
63    ///
64    /// The position moved by the given offset.
65    pub fn forward(&self, offset: usize) -> Self {
66        Self { source: self.source, offset: self.offset + offset }
67    }
68
69    /// Return the position moved back by the given offset.
70    ///
71    /// # Parameters
72    ///
73    /// - `offset`: The offset to move the position back by.
74    ///
75    /// # Returns
76    ///
77    /// The position moved back by the given offset.
78    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}