1use std::ops::Range;
2
3use serde::{Deserialize, Serialize};
4
5use crate::CharStringExt;
6
7#[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 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 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 pub fn push_by(&mut self, by: usize) {
88 self.start += by;
89 self.end += by;
90 }
91
92 pub fn pull_by(&mut self, by: usize) {
94 self.start -= by;
95 self.end -= by;
96 }
97
98 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 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 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}