castle_input_cursor/
position.rs

1//! This module implements the `Pos` structure, which represents a position in the source code.
2
3use std::{cmp::Ordering, fmt, num::NonZeroU32};
4
5/// A position in the source code.
6///
7/// Stores both the column number and the line number.
8///
9/// Note that spans are of the form [begining, end) i.e. that the begining position is inclusive and the end position is exclusive.
10/// See test check_positions from syntax/lexer/tests.rs for an example.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
12pub struct Position {
13    /// Line number.
14    pub line_number: NonZeroU32,
15    /// Column number.
16    pub column_number: NonZeroU32,
17}
18
19impl Position {
20    /// Creates a new `Position`.
21    #[inline]
22    #[track_caller]
23    pub fn new(line_number: u32, column_number: u32) -> Self {
24        Self {
25            line_number: NonZeroU32::new(line_number).expect("line number cannot be 0"),
26            column_number: NonZeroU32::new(column_number).expect("column number cannot be 0"),
27        }
28    }
29
30    /// Gets the line number of the position.
31    #[inline]
32    pub fn line_number(self) -> u32 {
33        self.line_number.get()
34    }
35
36    /// Gets the column number of the position.
37    #[inline]
38    pub fn column_number(self) -> u32 {
39        self.column_number.get()
40    }
41}
42
43impl fmt::Display for Position {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        write!(f, "{}:{}", self.line_number, self.column_number)
46    }
47}
48
49/// A span in the source code.
50///
51/// Stores a start position and an end position.
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
53pub struct Span {
54    pub start: Position,
55    pub end: Position,
56}
57
58impl Span {
59    /// Creates a new `Span`.
60    #[inline]
61    #[track_caller]
62    pub fn new(start: Position, end: Position) -> Self {
63        assert!(start <= end, "a span cannot start after its end");
64
65        Self { start, end }
66    }
67
68    /// Gets the starting position of the span.
69    #[inline]
70    pub fn start(self) -> Position {
71        self.start
72    }
73
74    /// Gets the final position of the span.
75    #[inline]
76    pub fn end(self) -> Position {
77        self.end
78    }
79
80    /// Checks if this span inclusively contains another span or position.
81    #[inline]
82    pub fn contains<S>(self, other: S) -> bool
83    where
84        S: Into<Self>,
85    {
86        let other = other.into();
87        self.start <= other.start && self.end >= other.end
88    }
89}
90
91impl From<Position> for Span {
92    fn from(pos: Position) -> Self {
93        Self {
94            start: pos,
95            end: pos,
96        }
97    }
98}
99
100impl PartialOrd for Span {
101    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
102        if self == other {
103            Some(Ordering::Equal)
104        } else if self.end < other.start {
105            Some(Ordering::Less)
106        } else if self.start > other.end {
107            Some(Ordering::Greater)
108        } else {
109            None
110        }
111    }
112}
113
114impl fmt::Display for Span {
115    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116        write!(f, "[{}..{}]", self.start, self.end)
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use super::{Position, Span};
123
124    /// Checks that we cannot create a position with 0 as the column.
125    #[test]
126    #[should_panic]
127    fn invalid_position_column() {
128        Position::new(10, 0);
129    }
130
131    /// Checks that we cannot create a position with 0 as the line.
132    #[test]
133    #[should_panic]
134    fn invalid_position_line() {
135        Position::new(0, 10);
136    }
137
138    /// Checks that the `PartialEq` implementation of `Position` is consistent.
139    #[test]
140    fn position_equality() {
141        assert_eq!(Position::new(10, 50), Position::new(10, 50));
142        assert_ne!(Position::new(10, 50), Position::new(10, 51));
143        assert_ne!(Position::new(10, 50), Position::new(11, 50));
144        assert_ne!(Position::new(10, 50), Position::new(11, 51));
145    }
146
147    /// Checks that the `PartialOrd` implementation of `Position` is consistent.
148    #[test]
149    fn position_order() {
150        assert!(Position::new(10, 50) < Position::new(10, 51));
151        assert!(Position::new(9, 50) < Position::new(10, 50));
152        assert!(Position::new(10, 50) < Position::new(11, 51));
153        assert!(Position::new(10, 50) < Position::new(11, 49));
154
155        assert!(Position::new(10, 51) > Position::new(10, 50));
156        assert!(Position::new(10, 50) > Position::new(9, 50));
157        assert!(Position::new(11, 51) > Position::new(10, 50));
158        assert!(Position::new(11, 49) > Position::new(10, 50));
159    }
160
161    /// Checks that the position getters actually retreive correct values.
162    #[test]
163    fn position_getters() {
164        let pos = Position::new(10, 50);
165        assert_eq!(pos.line_number(), 10);
166        assert_eq!(pos.column_number(), 50);
167    }
168
169    /// Checks that the string representation of a position is correct.
170    #[test]
171    fn position_to_string() {
172        let pos = Position::new(10, 50);
173
174        assert_eq!("10:50", pos.to_string());
175        assert_eq!("10:50", format!("{}", pos));
176    }
177
178    /// Checks that we cannot create an invalid span.
179    #[test]
180    #[should_panic]
181    fn invalid_span() {
182        let a = Position::new(10, 30);
183        let b = Position::new(10, 50);
184        Span::new(b, a);
185    }
186
187    /// Checks that we can create valid spans.
188    #[test]
189    fn span_creation() {
190        let a = Position::new(10, 30);
191        let b = Position::new(10, 50);
192
193        let _ = Span::new(a, b);
194        let _ = Span::new(a, a);
195        let _ = Span::from(a);
196    }
197
198    /// Checks that the `PartialEq` implementation of `Span` is consistent.
199    #[test]
200    fn span_equality() {
201        let a = Position::new(10, 50);
202        let b = Position::new(10, 52);
203        let c = Position::new(11, 20);
204
205        let span_ab = Span::new(a, b);
206        let span_ab_2 = Span::new(a, b);
207        let span_ac = Span::new(a, c);
208        let span_bc = Span::new(b, c);
209
210        assert_eq!(span_ab, span_ab_2);
211        assert_ne!(span_ab, span_ac);
212        assert_ne!(span_ab, span_bc);
213        assert_ne!(span_bc, span_ac);
214
215        let span_a = Span::from(a);
216        let span_aa = Span::new(a, a);
217
218        assert_eq!(span_a, span_aa);
219    }
220
221    /// Checks that the getters retrieve the correct value.
222    #[test]
223    fn span_getters() {
224        let a = Position::new(10, 50);
225        let b = Position::new(10, 52);
226
227        let span = Span::new(a, b);
228
229        assert_eq!(span.start(), a);
230        assert_eq!(span.end(), b);
231    }
232
233    /// Checks that the `Span::contains()` method works properly.
234    #[test]
235    fn span_contains() {
236        let a = Position::new(10, 50);
237        let b = Position::new(10, 52);
238        let c = Position::new(11, 20);
239        let d = Position::new(12, 5);
240
241        let span_ac = Span::new(a, c);
242        assert!(span_ac.contains(b));
243
244        let span_ab = Span::new(a, b);
245        let span_cd = Span::new(c, d);
246
247        assert!(!span_ab.contains(span_cd));
248        assert!(span_ab.contains(b));
249
250        let span_ad = Span::new(a, d);
251        let span_bc = Span::new(b, c);
252
253        assert!(span_ad.contains(span_bc));
254        assert!(!span_bc.contains(span_ad));
255
256        let span_ac = Span::new(a, c);
257        let span_bd = Span::new(b, d);
258
259        assert!(!span_ac.contains(span_bd));
260        assert!(!span_bd.contains(span_ac));
261    }
262
263    /// Checks that the string representation of a span is correct.
264    #[test]
265    fn span_to_string() {
266        let a = Position::new(10, 50);
267        let b = Position::new(11, 20);
268        let span = Span::new(a, b);
269
270        assert_eq!("[10:50..11:20]", span.to_string());
271        assert_eq!("[10:50..11:20]", format!("{}", span));
272    }
273
274    /// Checks that the ordering of spans is correct.
275    #[test]
276    fn span_ordering() {
277        let a = Position::new(10, 50);
278        let b = Position::new(10, 52);
279        let c = Position::new(11, 20);
280        let d = Position::new(12, 5);
281
282        let span_ab = Span::new(a, b);
283        let span_cd = Span::new(c, d);
284
285        assert!(span_ab < span_cd);
286        assert!(span_cd > span_ab);
287    }
288}