1use std::{cmp::Ordering, fmt, num::NonZeroU32};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
12pub struct Position {
13 pub line_number: NonZeroU32,
15 pub column_number: NonZeroU32,
17}
18
19impl Position {
20 #[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 #[inline]
32 pub fn line_number(self) -> u32 {
33 self.line_number.get()
34 }
35
36 #[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#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
53pub struct Span {
54 pub start: Position,
55 pub end: Position,
56}
57
58impl Span {
59 #[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 #[inline]
70 pub fn start(self) -> Position {
71 self.start
72 }
73
74 #[inline]
76 pub fn end(self) -> Position {
77 self.end
78 }
79
80 #[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 #[test]
126 #[should_panic]
127 fn invalid_position_column() {
128 Position::new(10, 0);
129 }
130
131 #[test]
133 #[should_panic]
134 fn invalid_position_line() {
135 Position::new(0, 10);
136 }
137
138 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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}