harper_core/
span.rs

1use std::ops::Range;
2
3use serde::{Deserialize, Serialize};
4
5/// A window in a [`char`] sequence.
6#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
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        if start > end {
15            panic!("{} > {}", start, end);
16        }
17        Self { start, end }
18    }
19
20    pub fn new_with_len(start: usize, len: usize) -> Self {
21        Self {
22            start,
23            end: start + len,
24        }
25    }
26
27    pub fn len(&self) -> usize {
28        self.end - self.start
29    }
30
31    pub fn is_empty(&self) -> bool {
32        self.len() == 0
33    }
34
35    pub fn contains(&self, idx: usize) -> bool {
36        assert!(self.start <= self.end);
37
38        self.start <= idx && idx < self.end
39    }
40
41    pub fn overlaps_with(&self, other: Self) -> bool {
42        (self.start < other.end) && (other.start < self.end)
43    }
44
45    /// Get the associated content. Will return [`None`] if any aspect is
46    /// invalid.
47    pub fn try_get_content<'a>(&self, source: &'a [char]) -> Option<&'a [char]> {
48        if (self.start > self.end) || (self.start >= source.len()) || (self.end > source.len()) {
49            if self.is_empty() {
50                return Some(&source[0..0]);
51            }
52            return None;
53        }
54
55        Some(&source[self.start..self.end])
56    }
57
58    /// Get the associated content. Will panic if any aspect is invalid.
59    pub fn get_content<'a>(&self, source: &'a [char]) -> &'a [char] {
60        self.try_get_content(source).unwrap()
61    }
62
63    pub fn get_content_string(&self, source: &[char]) -> String {
64        String::from_iter(self.get_content(source))
65    }
66
67    pub fn set_len(&mut self, length: usize) {
68        self.end = self.start + length;
69    }
70
71    pub fn with_len(&self, length: usize) -> Self {
72        let mut cloned = *self;
73        cloned.set_len(length);
74        cloned
75    }
76
77    // Add an amount to both [`Self::start`] and [`Self::end`]
78    pub fn push_by(&mut self, by: usize) {
79        self.start += by;
80        self.end += by;
81    }
82
83    // Subtract an amount to both [`Self::start`] and [`Self::end`]
84    pub fn pull_by(&mut self, by: usize) {
85        self.start -= by;
86        self.end -= by;
87    }
88
89    // Add an amount to a copy of both [`Self::start`] and [`Self::end`]
90    pub fn pushed_by(&self, by: usize) -> Self {
91        let mut clone = *self;
92        clone.start += by;
93        clone.end += by;
94        clone
95    }
96
97    // Subtract an amount to a copy of both [`Self::start`] and [`Self::end`]
98    pub fn pulled_by(&self, by: usize) -> Option<Self> {
99        if by > self.start {
100            return None;
101        }
102
103        let mut clone = *self;
104        clone.start -= by;
105        clone.end -= by;
106        Some(clone)
107    }
108
109    // Add an amount a copy of both [`Self::start`] and [`Self::end`]
110    pub fn with_offset(&self, by: usize) -> Self {
111        let mut clone = *self;
112        clone.push_by(by);
113        clone
114    }
115}
116
117impl From<Range<usize>> for Span {
118    fn from(value: Range<usize>) -> Self {
119        Self::new(value.start, value.end)
120    }
121}
122
123impl From<Span> for Range<usize> {
124    fn from(value: Span) -> Self {
125        value.start..value.end
126    }
127}
128
129impl IntoIterator for Span {
130    type Item = usize;
131
132    type IntoIter = Range<usize>;
133
134    fn into_iter(self) -> Self::IntoIter {
135        self.start..self.end
136    }
137}
138
139#[cfg(test)]
140mod tests {
141    use crate::Span;
142
143    #[test]
144    fn overlaps() {
145        assert!(Span::new(0, 5).overlaps_with(Span::new(3, 6)));
146        assert!(Span::new(0, 5).overlaps_with(Span::new(2, 3)));
147        assert!(Span::new(0, 5).overlaps_with(Span::new(4, 5)));
148        assert!(Span::new(0, 5).overlaps_with(Span::new(4, 4)));
149
150        assert!(!Span::new(0, 3).overlaps_with(Span::new(3, 5)));
151    }
152}