Skip to main content

rl_utils/
span.rs

1use std::ops::Range;
2
3/// A byte-offset range into the source string.
4///
5/// Used for pointing error reports at exact source locations.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
7pub struct Span {
8    pub start: usize,
9    pub end: usize,
10}
11
12impl Span {
13    pub fn new(start: usize, end: usize) -> Self {
14        Self { start, end }
15    }
16
17    /// A sentinel span used when no real location is known.
18    pub fn dummy() -> Self {
19        Self { start: 0, end: 0 }
20    }
21
22    /// Span covering both `self` and `other` (and everything between).
23    pub fn join(self, other: Self) -> Self {
24        Self {
25            start: self.start.min(other.start),
26            end: self.end.max(other.end),
27        }
28    }
29}
30
31impl From<Span> for Range<usize> {
32    fn from(s: Span) -> Self {
33        s.start..s.end
34    }
35}
36
37impl From<Range<usize>> for Span {
38    fn from(r: Range<usize>) -> Self {
39        Self {
40            start: r.start,
41            end: r.end,
42        }
43    }
44}
45
46#[cfg(test)]
47mod tests {
48    use super::Span;
49    use std::ops::Range;
50
51    const DEFAULT_START: usize = 0;
52    const DEFAULT_END: usize = 10;
53
54    #[test]
55    fn span_basic() {
56        let span = Span::new(DEFAULT_START, DEFAULT_END);
57        assert_eq!(span.start, DEFAULT_START);
58        assert_eq!(span.end, DEFAULT_END);
59    }
60
61    #[test]
62    fn span_dummy() {
63        let span = Span::dummy();
64        assert_eq!(span.start, DEFAULT_START);
65        assert_eq!(span.end, DEFAULT_START);
66    }
67
68    #[test]
69    fn span_join_non_overlapping() {
70        let span = Span::new(DEFAULT_START, DEFAULT_START + 3);
71        let span_other = Span::new(DEFAULT_START, DEFAULT_START + 15);
72        assert_eq!(
73            span.join(span_other),
74            Span::new(DEFAULT_START, DEFAULT_START + 15)
75        );
76    }
77
78    #[test]
79    fn span_join_overlapping() {
80        let span = Span::new(DEFAULT_START, DEFAULT_START + 5);
81        let span_other = Span::new(DEFAULT_START + 10, DEFAULT_START + 15);
82        assert_eq!(
83            span.join(span_other),
84            Span::new(DEFAULT_START, DEFAULT_START + 15)
85        );
86    }
87
88    #[test]
89    fn span_from_range() {
90        let range = DEFAULT_START..DEFAULT_END;
91        assert_eq!(Span::new(DEFAULT_START, DEFAULT_END), Span::from(range));
92    }
93
94    #[test]
95    fn range_from_span() {
96        let span = Span::new(DEFAULT_START, DEFAULT_END);
97        assert_eq!(DEFAULT_START..DEFAULT_END, Range::from(span));
98    }
99
100    #[test]
101    fn range_from_span_using_into() {
102        let span = Span::new(DEFAULT_START, DEFAULT_END);
103        let range: Range<usize> = span.into();
104        assert_eq!(range, DEFAULT_START..DEFAULT_END);
105    }
106}