1use std::{cmp::Ordering, fmt, num::NonZeroU32};
4
5#[cfg(feature = "deser")]
6use serde::{Deserialize, Serialize};
7
8#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
18#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
19pub struct Position {
20 line_number: NonZeroU32,
22 column_number: NonZeroU32,
24}
25
26impl Position {
27 #[inline]
29 #[track_caller]
30 pub fn new(line_number: u32, column_number: u32) -> Self {
31 Self {
32 line_number: NonZeroU32::new(line_number).expect("line number cannot be 0"),
33 column_number: NonZeroU32::new(column_number).expect("column number cannot be 0"),
34 }
35 }
36
37 #[inline]
39 pub fn line_number(self) -> u32 {
40 self.line_number.get()
41 }
42
43 #[inline]
45 pub fn column_number(self) -> u32 {
46 self.column_number.get()
47 }
48}
49
50impl fmt::Display for Position {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 write!(f, "{}:{}", self.line_number, self.column_number)
53 }
54}
55
56#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61pub struct Span {
62 start: Position,
63 end: Position,
64}
65
66impl Span {
67 #[inline]
69 #[track_caller]
70 pub fn new(start: Position, end: Position) -> Self {
71 assert!(start <= end, "a span cannot start after its end");
72
73 Self { start, end }
74 }
75
76 #[inline]
78 pub fn start(self) -> Position {
79 self.start
80 }
81
82 #[inline]
84 pub fn end(self) -> Position {
85 self.end
86 }
87
88 #[inline]
90 pub fn contains<S>(self, other: S) -> bool
91 where
92 S: Into<Self>,
93 {
94 let other = other.into();
95 self.start <= other.start && self.end >= other.end
96 }
97}
98
99impl From<Position> for Span {
100 fn from(pos: Position) -> Self {
101 Self {
102 start: pos,
103 end: pos,
104 }
105 }
106}
107
108impl PartialOrd for Span {
109 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
110 if self == other {
111 Some(Ordering::Equal)
112 } else if self.end < other.start {
113 Some(Ordering::Less)
114 } else if self.start > other.end {
115 Some(Ordering::Greater)
116 } else {
117 None
118 }
119 }
120}
121
122impl fmt::Display for Span {
123 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124 write!(f, "[{}..{}]", self.start, self.end)
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::{Position, Span};
131
132 #[test]
134 #[should_panic]
135 fn invalid_position_column() {
136 Position::new(10, 0);
137 }
138
139 #[test]
141 #[should_panic]
142 fn invalid_position_line() {
143 Position::new(0, 10);
144 }
145
146 #[test]
148 fn position_equality() {
149 assert_eq!(Position::new(10, 50), Position::new(10, 50));
150 assert_ne!(Position::new(10, 50), Position::new(10, 51));
151 assert_ne!(Position::new(10, 50), Position::new(11, 50));
152 assert_ne!(Position::new(10, 50), Position::new(11, 51));
153 }
154
155 #[test]
157 fn position_order() {
158 assert!(Position::new(10, 50) < Position::new(10, 51));
159 assert!(Position::new(9, 50) < Position::new(10, 50));
160 assert!(Position::new(10, 50) < Position::new(11, 51));
161 assert!(Position::new(10, 50) < Position::new(11, 49));
162
163 assert!(Position::new(10, 51) > Position::new(10, 50));
164 assert!(Position::new(10, 50) > Position::new(9, 50));
165 assert!(Position::new(11, 51) > Position::new(10, 50));
166 assert!(Position::new(11, 49) > Position::new(10, 50));
167 }
168
169 #[test]
171 fn position_getters() {
172 let pos = Position::new(10, 50);
173 assert_eq!(pos.line_number(), 10);
174 assert_eq!(pos.column_number(), 50);
175 }
176
177 #[test]
179 fn position_to_string() {
180 let pos = Position::new(10, 50);
181
182 assert_eq!("10:50", pos.to_string());
183 assert_eq!("10:50", format!("{}", pos));
184 }
185
186 #[test]
188 #[should_panic]
189 fn invalid_span() {
190 let a = Position::new(10, 30);
191 let b = Position::new(10, 50);
192 Span::new(b, a);
193 }
194
195 #[test]
197 fn span_creation() {
198 let a = Position::new(10, 30);
199 let b = Position::new(10, 50);
200
201 let _ = Span::new(a, b);
202 let _ = Span::new(a, a);
203 let _ = Span::from(a);
204 }
205
206 #[test]
208 fn span_equality() {
209 let a = Position::new(10, 50);
210 let b = Position::new(10, 52);
211 let c = Position::new(11, 20);
212
213 let span_ab = Span::new(a, b);
214 let span_ab_2 = Span::new(a, b);
215 let span_ac = Span::new(a, c);
216 let span_bc = Span::new(b, c);
217
218 assert_eq!(span_ab, span_ab_2);
219 assert_ne!(span_ab, span_ac);
220 assert_ne!(span_ab, span_bc);
221 assert_ne!(span_bc, span_ac);
222
223 let span_a = Span::from(a);
224 let span_aa = Span::new(a, a);
225
226 assert_eq!(span_a, span_aa);
227 }
228
229 #[test]
231 fn span_getters() {
232 let a = Position::new(10, 50);
233 let b = Position::new(10, 52);
234
235 let span = Span::new(a, b);
236
237 assert_eq!(span.start(), a);
238 assert_eq!(span.end(), b);
239 }
240
241 #[test]
243 fn span_contains() {
244 let a = Position::new(10, 50);
245 let b = Position::new(10, 52);
246 let c = Position::new(11, 20);
247 let d = Position::new(12, 5);
248
249 let span_ac = Span::new(a, c);
250 assert!(span_ac.contains(b));
251
252 let span_ab = Span::new(a, b);
253 let span_cd = Span::new(c, d);
254
255 assert!(!span_ab.contains(span_cd));
256 assert!(span_ab.contains(b));
257
258 let span_ad = Span::new(a, d);
259 let span_bc = Span::new(b, c);
260
261 assert!(span_ad.contains(span_bc));
262 assert!(!span_bc.contains(span_ad));
263
264 let span_ac = Span::new(a, c);
265 let span_bd = Span::new(b, d);
266
267 assert!(!span_ac.contains(span_bd));
268 assert!(!span_bd.contains(span_ac));
269 }
270
271 #[test]
273 fn span_to_string() {
274 let a = Position::new(10, 50);
275 let b = Position::new(11, 20);
276 let span = Span::new(a, b);
277
278 assert_eq!("[10:50..11:20]", span.to_string());
279 assert_eq!("[10:50..11:20]", format!("{}", span));
280 }
281
282 #[test]
284 fn span_ordering() {
285 let a = Position::new(10, 50);
286 let b = Position::new(10, 52);
287 let c = Position::new(11, 20);
288 let d = Position::new(12, 5);
289
290 let span_ab = Span::new(a, b);
291 let span_cd = Span::new(c, d);
292
293 assert!(span_ab < span_cd);
294 assert!(span_cd > span_ab);
295 }
296}