perl_position_tracking/
span.rs1use 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}