1#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
9pub struct Span {
10 pub start: u32,
11 pub end: u32,
12}
13
14impl Span {
15 pub fn new(start: u32, end: u32) -> Self {
22 assert!(end >= start, "span end {end} precedes start {start}");
23 Self { start, end }
24 }
25
26 pub fn at(offset: u32) -> Self {
28 Self {
29 start: offset,
30 end: offset,
31 }
32 }
33
34 pub fn is_empty(self) -> bool {
35 self.start == self.end
36 }
37
38 pub fn to(self, other: Span) -> Span {
40 Span {
41 start: self.start.min(other.start),
42 end: self.end.max(other.end),
43 }
44 }
45
46 pub fn contains(self, offset: u32) -> bool {
47 offset >= self.start && offset < self.end
48 }
49
50 pub fn contains_span(self, other: Span) -> bool {
51 other.start >= self.start && other.end <= self.end
52 }
53
54 pub fn as_range(self) -> std::ops::Range<usize> {
55 self.start as usize..self.end as usize
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use super::Span;
62
63 #[test]
64 fn union_covers_both() {
65 let a = Span::new(2, 5);
66 let b = Span::new(10, 12);
67 assert_eq!(a.to(b), Span::new(2, 12));
68 assert_eq!(b.to(a), Span::new(2, 12));
69 }
70
71 #[test]
72 fn empty_span_contains_nothing() {
73 let s = Span::at(4);
74 assert!(s.is_empty());
75 assert!(!s.contains(4));
76 }
77
78 #[test]
79 fn a_span_contains_itself_and_spans_strictly_inside_it() {
80 let outer = Span::new(2, 8);
81 assert!(outer.contains_span(outer));
82 assert!(outer.contains_span(Span::new(3, 7)));
83 }
84
85 #[test]
86 fn a_span_does_not_contain_one_reaching_past_either_edge() {
87 let outer = Span::new(2, 8);
88 assert!(!outer.contains_span(Span::new(1, 7)));
89 assert!(!outer.contains_span(Span::new(3, 9)));
90 }
91
92 #[test]
93 fn end_is_exclusive() {
94 let s = Span::new(1, 3);
95 assert!(s.contains(1));
96 assert!(s.contains(2));
97 assert!(!s.contains(3));
98 }
99}