1use serde::{Deserialize, Serialize};
9use std::fmt;
10use std::ops::Range;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
39pub struct ByteSpan {
40 pub start: usize,
42 pub end: usize,
44}
45
46impl ByteSpan {
47 #[inline]
53 pub fn new(start: usize, end: usize) -> Self {
54 debug_assert!(start <= end, "ByteSpan: start ({}) > end ({})", start, end);
55 Self { start, end }
56 }
57
58 #[inline]
60 pub const fn empty(pos: usize) -> Self {
61 Self { start: pos, end: pos }
62 }
63
64 #[inline]
66 pub fn whole(source: &str) -> Self {
67 Self { start: 0, end: source.len() }
68 }
69
70 #[inline]
72 pub const fn len(&self) -> usize {
73 self.end - self.start
74 }
75
76 #[inline]
78 pub const fn is_empty(&self) -> bool {
79 self.start == self.end
80 }
81
82 #[inline]
84 pub const fn contains(&self, offset: usize) -> bool {
85 offset >= self.start && offset < self.end
86 }
87
88 #[inline]
90 pub const fn contains_span(&self, other: ByteSpan) -> bool {
91 self.start <= other.start && other.end <= self.end
92 }
93
94 #[inline]
96 pub const fn overlaps(&self, other: ByteSpan) -> bool {
97 self.start < other.end && other.start < self.end
98 }
99
100 pub fn intersection(&self, other: ByteSpan) -> Option<ByteSpan> {
102 let start = self.start.max(other.start);
103 let end = self.end.min(other.end);
104 if start < end { Some(ByteSpan { start, end }) } else { None }
105 }
106
107 #[inline]
109 pub fn union(&self, other: ByteSpan) -> ByteSpan {
110 ByteSpan { start: self.start.min(other.start), end: self.end.max(other.end) }
111 }
112
113 #[inline]
119 pub fn slice<'a>(&self, source: &'a str) -> &'a str {
120 &source[self.start..self.end]
121 }
122
123 #[inline]
125 pub fn try_slice<'a>(&self, source: &'a str) -> Option<&'a str> {
126 source.get(self.start..self.end)
127 }
128
129 #[inline]
131 pub const fn to_range(&self) -> Range<usize> {
132 self.start..self.end
133 }
134}
135
136impl fmt::Display for ByteSpan {
137 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138 write!(f, "{}..{}", self.start, self.end)
139 }
140}
141
142impl From<Range<usize>> for ByteSpan {
143 #[inline]
144 fn from(range: Range<usize>) -> Self {
145 Self::new(range.start, range.end)
146 }
147}
148
149impl From<ByteSpan> for Range<usize> {
150 #[inline]
151 fn from(span: ByteSpan) -> Self {
152 span.start..span.end
153 }
154}
155
156impl From<(usize, usize)> for ByteSpan {
157 #[inline]
158 fn from((start, end): (usize, usize)) -> Self {
159 Self::new(start, end)
160 }
161}
162
163impl From<ByteSpan> for (usize, usize) {
164 #[inline]
165 fn from(span: ByteSpan) -> Self {
166 (span.start, span.end)
167 }
168}
169
170pub type SourceLocation = ByteSpan;
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178
179 #[test]
180 fn test_byte_span_basics() {
181 let span = ByteSpan::new(5, 10);
182 assert_eq!(span.start, 5);
183 assert_eq!(span.end, 10);
184 assert_eq!(span.len(), 5);
185 assert!(!span.is_empty());
186 }
187
188 #[test]
189 fn test_empty_span() {
190 let span = ByteSpan::empty(5);
191 assert_eq!(span.start, 5);
192 assert_eq!(span.end, 5);
193 assert_eq!(span.len(), 0);
194 assert!(span.is_empty());
195 }
196
197 #[test]
198 fn test_contains() {
199 let span = ByteSpan::new(5, 10);
200 assert!(!span.contains(4));
201 assert!(span.contains(5));
202 assert!(span.contains(9));
203 assert!(!span.contains(10)); }
205
206 #[test]
207 fn test_contains_span() {
208 let outer = ByteSpan::new(0, 20);
209 let inner = ByteSpan::new(5, 15);
210 let partial = ByteSpan::new(15, 25);
211
212 assert!(outer.contains_span(inner));
213 assert!(!inner.contains_span(outer));
214 assert!(!outer.contains_span(partial));
215 }
216
217 #[test]
218 fn test_overlaps() {
219 let a = ByteSpan::new(0, 10);
220 let b = ByteSpan::new(5, 15);
221 let c = ByteSpan::new(10, 20);
222 let d = ByteSpan::new(15, 25);
223
224 assert!(a.overlaps(b)); assert!(!a.overlaps(c)); assert!(!a.overlaps(d)); }
228
229 #[test]
230 fn test_intersection() {
231 let a = ByteSpan::new(0, 10);
232 let b = ByteSpan::new(5, 15);
233
234 assert_eq!(a.intersection(b), Some(ByteSpan::new(5, 10)));
235 assert_eq!(a.intersection(ByteSpan::new(10, 20)), None);
236 }
237
238 #[test]
239 fn test_union() {
240 let a = ByteSpan::new(0, 10);
241 let b = ByteSpan::new(5, 15);
242
243 assert_eq!(a.union(b), ByteSpan::new(0, 15));
244 }
245
246 #[test]
247 fn test_slice() {
248 let source = "hello world";
249 let span = ByteSpan::new(0, 5);
250 assert_eq!(span.slice(source), "hello");
251 }
252
253 #[test]
254 fn test_conversions() {
255 let span = ByteSpan::new(5, 10);
256
257 let range: Range<usize> = span.into();
259 assert_eq!(range, 5..10);
260 let span2: ByteSpan = (5..10).into();
261 assert_eq!(span, span2);
262
263 let tuple: (usize, usize) = span.into();
265 assert_eq!(tuple, (5, 10));
266 let span3: ByteSpan = (5, 10).into();
267 assert_eq!(span, span3);
268 }
269
270 #[test]
271 fn test_display() {
272 let span = ByteSpan::new(5, 10);
273 assert_eq!(format!("{}", span), "5..10");
274 }
275
276 #[test]
279 fn test_intersection_non_overlapping_returns_none() {
280 let a = ByteSpan::new(0, 5);
281 let b = ByteSpan::new(10, 20);
282 assert_eq!(a.intersection(b), None, "disjoint spans must have no intersection");
283 assert_eq!(b.intersection(a), None, "disjoint spans must have no intersection (reversed)");
284 }
285
286 #[test]
287 fn test_intersection_identical_returns_same() {
288 let a = ByteSpan::new(3, 9);
289 assert_eq!(a.intersection(a), Some(a), "identical spans intersect as themselves");
290 }
291
292 #[test]
293 fn test_union_identical_returns_same() {
294 let a = ByteSpan::new(3, 9);
295 assert_eq!(a.union(a), a, "union of identical span is itself");
296 }
297
298 #[test]
299 fn test_intersection_nested_equals_inner() {
300 let outer = ByteSpan::new(0, 20);
301 let inner = ByteSpan::new(5, 15);
302 assert_eq!(
303 outer.intersection(inner),
304 Some(inner),
305 "intersection of outer with inner must equal inner"
306 );
307 assert_eq!(inner.intersection(outer), Some(inner), "intersection is commutative");
308 }
309
310 #[test]
311 fn test_union_nested_equals_outer() {
312 let outer = ByteSpan::new(0, 20);
313 let inner = ByteSpan::new(5, 15);
314 assert_eq!(outer.union(inner), outer, "union of nested spans must equal outer");
315 assert_eq!(inner.union(outer), outer, "union is commutative");
316 }
317
318 #[test]
319 fn test_intersection_adjacent_returns_none() {
320 let a = ByteSpan::new(0, 5);
323 let b = ByteSpan::new(5, 10);
324 assert_eq!(a.intersection(b), None, "adjacent spans have no overlap: intersection is None");
325 }
326
327 #[test]
328 fn test_union_adjacent_spans_is_minimal_cover() {
329 let a = ByteSpan::new(0, 5);
330 let b = ByteSpan::new(5, 10);
331 assert_eq!(
332 a.union(b),
333 ByteSpan::new(0, 10),
334 "union of adjacent spans covers both without gap"
335 );
336 }
337
338 #[test]
339 fn test_intersection_empty_span_returns_none() {
340 let empty = ByteSpan::empty(5);
344 let full = ByteSpan::new(3, 10);
345
346 assert_eq!(
348 empty.intersection(full),
349 None,
350 "zero-length span has no bytes, intersection must be None"
351 );
352 assert_eq!(
353 full.intersection(empty),
354 None,
355 "zero-length span has no bytes, intersection must be None (reversed)"
356 );
357
358 assert_eq!(
360 empty.intersection(empty),
361 None,
362 "two zero-length spans at the same point have no intersection"
363 );
364 }
365
366 #[test]
367 fn test_union_empty_span_equals_non_empty() {
368 let empty = ByteSpan::empty(5);
369 let full = ByteSpan::new(3, 10);
370 assert_eq!(
372 empty.union(full),
373 ByteSpan::new(3, 10),
374 "union with empty span at interior point covers at least the full span"
375 );
376 }
377
378 #[test]
379 fn test_union_non_overlapping_is_minimal_cover() {
380 let a = ByteSpan::new(0, 5);
383 let b = ByteSpan::new(10, 20);
384 assert_eq!(
385 a.union(b),
386 ByteSpan::new(0, 20),
387 "union of non-overlapping spans spans the full range"
388 );
389 assert_eq!(b.union(a), ByteSpan::new(0, 20), "union is commutative");
390 }
391}