harper_core/
span.rs

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