1#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
22pub struct SourcePosition {
23 pub line: usize,
25 pub column: usize,
27}
28
29impl SourcePosition {
30 #[must_use]
32 pub fn new(line: usize, column: usize) -> Self {
33 Self { line, column }
34 }
35
36 #[must_use]
38 pub fn start() -> Self {
39 Self { line: 1, column: 0 }
40 }
41
42 #[must_use]
44 pub fn end() -> Self {
45 Self {
46 line: usize::MAX,
47 column: usize::MAX,
48 }
49 }
50}
51
52#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct SourceRange {
55 pub start: SourcePosition,
57 pub end: SourcePosition,
59}
60
61impl SourceRange {
62 #[must_use]
64 pub fn new(start: SourcePosition, end: SourcePosition) -> Self {
65 Self { start, end }
66 }
67
68 #[must_use]
70 pub fn line(line: usize) -> Self {
71 Self {
72 start: SourcePosition::new(line, 0),
73 end: SourcePosition::new(line + 1, 0),
74 }
75 }
76
77 #[must_use]
79 pub fn point(pos: SourcePosition) -> Self {
80 Self {
81 start: pos,
82 end: pos,
83 }
84 }
85
86 #[must_use]
88 pub fn overlaps(&self, other: &SourceRange) -> bool {
89 !(self.end <= other.start || other.end <= self.start)
91 }
92
93 #[must_use]
95 pub fn contains(&self, other: &SourceRange) -> bool {
96 self.start <= other.start && other.end <= self.end
97 }
98
99 #[must_use]
101 pub fn contains_position(&self, pos: &SourcePosition) -> bool {
102 self.start <= *pos && *pos < self.end
103 }
104
105 #[must_use]
107 pub fn is_empty(&self) -> bool {
108 self.start == self.end
109 }
110
111 #[must_use]
113 pub fn merge(&self, other: &SourceRange) -> SourceRange {
114 SourceRange {
115 start: self.start.min(other.start),
116 end: self.end.max(other.end),
117 }
118 }
119
120 #[must_use]
122 pub fn line_count(&self) -> usize {
123 self.end.line.saturating_sub(self.start.line)
124 }
125
126 #[must_use]
128 pub fn is_valid(&self) -> bool {
129 self.start <= self.end
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn test_source_position_new() {
139 let pos = SourcePosition::new(42, 10);
140 assert_eq!(pos.line, 42);
141 assert_eq!(pos.column, 10);
142 }
143
144 #[test]
145 fn test_source_position_start() {
146 let pos = SourcePosition::start();
147 assert_eq!(pos.line, 1);
148 assert_eq!(pos.column, 0);
149 }
150
151 #[test]
152 fn test_source_position_end() {
153 let pos = SourcePosition::end();
154 assert_eq!(pos.line, usize::MAX);
155 assert_eq!(pos.column, usize::MAX);
156 }
157
158 #[test]
159 fn test_source_position_ordering() {
160 let pos1 = SourcePosition::new(1, 5);
161 let pos2 = SourcePosition::new(1, 10);
162 let pos3 = SourcePosition::new(2, 0);
163
164 assert!(pos1 < pos2);
165 assert!(pos2 < pos3);
166 assert!(pos1 < pos3);
167 }
168
169 #[test]
170 fn test_source_position_equality() {
171 let pos1 = SourcePosition::new(5, 10);
172 let pos2 = SourcePosition::new(5, 10);
173 let pos3 = SourcePosition::new(5, 11);
174
175 assert_eq!(pos1, pos2);
176 assert_ne!(pos1, pos3);
177 }
178
179 #[test]
180 fn test_source_range_new() {
181 let start = SourcePosition::new(1, 0);
182 let end = SourcePosition::new(1, 10);
183 let range = SourceRange::new(start, end);
184
185 assert_eq!(range.start, start);
186 assert_eq!(range.end, end);
187 }
188
189 #[test]
190 fn test_source_range_line() {
191 let range = SourceRange::line(5);
192 assert_eq!(range.start.line, 5);
193 assert_eq!(range.start.column, 0);
194 assert_eq!(range.end.line, 6);
195 assert_eq!(range.end.column, 0);
196 }
197
198 #[test]
199 fn test_source_range_point() {
200 let pos = SourcePosition::new(3, 7);
201 let range = SourceRange::point(pos);
202 assert_eq!(range.start, pos);
203 assert_eq!(range.end, pos);
204 assert!(range.is_empty());
205 }
206
207 #[test]
208 fn test_range_overlaps_true() {
209 let range1 = SourceRange::new(SourcePosition::new(1, 0), SourcePosition::new(1, 10));
210 let range2 = SourceRange::new(SourcePosition::new(1, 5), SourcePosition::new(1, 15));
211
212 assert!(range1.overlaps(&range2));
213 assert!(range2.overlaps(&range1));
214 }
215
216 #[test]
217 fn test_range_overlaps_false() {
218 let range1 = SourceRange::new(SourcePosition::new(1, 0), SourcePosition::new(1, 5));
219 let range2 = SourceRange::new(SourcePosition::new(1, 10), SourcePosition::new(1, 15));
220
221 assert!(!range1.overlaps(&range2));
222 assert!(!range2.overlaps(&range1));
223 }
224
225 #[test]
226 fn test_range_overlaps_adjacent() {
227 let range1 = SourceRange::new(SourcePosition::new(1, 0), SourcePosition::new(1, 5));
228 let range2 = SourceRange::new(SourcePosition::new(1, 5), SourcePosition::new(1, 10));
229
230 assert!(!range1.overlaps(&range2));
232 }
233
234 #[test]
235 fn test_range_overlaps_contained() {
236 let range1 = SourceRange::new(SourcePosition::new(1, 0), SourcePosition::new(1, 20));
237 let range2 = SourceRange::new(SourcePosition::new(1, 5), SourcePosition::new(1, 15));
238
239 assert!(range1.overlaps(&range2));
240 assert!(range2.overlaps(&range1));
241 }
242
243 #[test]
244 fn test_range_contains_range() {
245 let outer = SourceRange::new(SourcePosition::new(1, 0), SourcePosition::new(3, 0));
246 let inner = SourceRange::new(SourcePosition::new(2, 0), SourcePosition::new(2, 10));
247
248 assert!(outer.contains(&inner));
249 assert!(!inner.contains(&outer));
250 }
251
252 #[test]
253 fn test_range_contains_position() {
254 let range = SourceRange::new(SourcePosition::new(1, 5), SourcePosition::new(1, 15));
255
256 assert!(range.contains_position(&SourcePosition::new(1, 5)));
257 assert!(range.contains_position(&SourcePosition::new(1, 10)));
258 assert!(!range.contains_position(&SourcePosition::new(1, 15))); assert!(!range.contains_position(&SourcePosition::new(1, 0)));
260 assert!(!range.contains_position(&SourcePosition::new(2, 0)));
261 }
262
263 #[test]
264 fn test_range_is_empty() {
265 let empty = SourceRange::point(SourcePosition::new(1, 5));
266 assert!(empty.is_empty());
267
268 let non_empty = SourceRange::new(SourcePosition::new(1, 5), SourcePosition::new(1, 10));
269 assert!(!non_empty.is_empty());
270 }
271
272 #[test]
273 fn test_range_merge() {
274 let range1 = SourceRange::new(SourcePosition::new(1, 0), SourcePosition::new(1, 10));
275 let range2 = SourceRange::new(SourcePosition::new(1, 5), SourcePosition::new(2, 0));
276
277 let merged = range1.merge(&range2);
278 assert_eq!(merged.start, SourcePosition::new(1, 0));
279 assert_eq!(merged.end, SourcePosition::new(2, 0));
280 }
281
282 #[test]
283 fn test_range_merge_disjoint() {
284 let range1 = SourceRange::new(SourcePosition::new(1, 0), SourcePosition::new(1, 5));
285 let range2 = SourceRange::new(SourcePosition::new(3, 0), SourcePosition::new(3, 5));
286
287 let merged = range1.merge(&range2);
288 assert_eq!(merged.start, SourcePosition::new(1, 0));
289 assert_eq!(merged.end, SourcePosition::new(3, 5));
290 }
291
292 #[test]
293 fn test_range_line_count() {
294 let range = SourceRange::new(SourcePosition::new(1, 0), SourcePosition::new(5, 0));
295 assert_eq!(range.line_count(), 4);
296
297 let single_line = SourceRange::new(SourcePosition::new(3, 5), SourcePosition::new(3, 10));
298 assert_eq!(single_line.line_count(), 0);
299 }
300
301 #[test]
302 fn test_range_is_valid() {
303 let valid = SourceRange::new(SourcePosition::new(1, 0), SourcePosition::new(2, 0));
304 assert!(valid.is_valid());
305
306 let invalid = SourceRange::new(SourcePosition::new(2, 0), SourcePosition::new(1, 0));
307 assert!(!invalid.is_valid());
308 }
309
310 #[test]
311 fn test_range_equality() {
312 let range1 = SourceRange::new(SourcePosition::new(1, 0), SourcePosition::new(1, 10));
313 let range2 = SourceRange::new(SourcePosition::new(1, 0), SourcePosition::new(1, 10));
314 let range3 = SourceRange::new(SourcePosition::new(1, 0), SourcePosition::new(1, 11));
315
316 assert_eq!(range1, range2);
317 assert_ne!(range1, range3);
318 }
319
320 #[test]
321 fn test_multiline_range() {
322 let range = SourceRange::new(SourcePosition::new(1, 5), SourcePosition::new(5, 10));
323
324 assert!(range.is_valid());
325 assert_eq!(range.line_count(), 4);
326 assert!(range.contains_position(&SourcePosition::new(3, 0)));
327 assert!(!range.contains_position(&SourcePosition::new(6, 0)));
328 }
329}