1use std::{fmt::Display, marker::PhantomData, ops::Range};
2
3use serde::{Deserialize, Serialize};
4
5use crate::Token;
6
7#[derive(Debug, Serialize, Deserialize, Default, PartialEq, Eq)]
19pub struct Span<T> {
20 pub start: usize,
22 pub end: usize,
27 span_type: PhantomData<T>,
28}
29
30impl<T> Span<T> {
31 pub const EMPTY: Self = Self {
33 start: 0,
34 end: 0,
35 span_type: PhantomData,
36 };
37
38 pub fn new(start: usize, end: usize) -> Self {
44 if start > end {
45 panic!("{start} > {end}");
46 }
47 Self {
48 start,
49 end,
50 span_type: PhantomData,
51 }
52 }
53
54 pub fn new_with_len(start: usize, len: usize) -> Self {
56 Self {
57 start,
58 end: start + len,
59 span_type: PhantomData,
60 }
61 }
62
63 pub fn len(&self) -> usize {
65 self.end - self.start
66 }
67
68 pub fn is_empty(&self) -> bool {
72 self.len() == 0
73 }
74
75 pub fn contains(&self, idx: usize) -> bool {
77 self.start <= idx && idx < self.end
78 }
79
80 pub fn overlaps_with(&self, other: Self) -> bool {
82 (self.start < other.end) && (other.start < self.end)
83 }
84
85 pub fn try_get_content<'a>(&self, source: &'a [T]) -> Option<&'a [T]> {
88 if self.is_empty() {
89 Some(&source[0..0])
90 } else {
91 source.get(self.start..self.end)
92 }
93 }
94
95 pub fn expand_to_include(&mut self, target: usize) {
100 if target < self.start {
101 self.start = target;
102 } else if target >= self.end {
103 self.end = target + 1;
104 }
105 }
106
107 pub fn get_content<'a>(&self, source: &'a [T]) -> &'a [T] {
109 match self.try_get_content(source) {
110 Some(v) => v,
111 None => panic!("Failed to get content for span."),
112 }
113 }
114
115 pub fn set_len(&mut self, length: usize) {
117 self.end = self.start + length;
118 }
119
120 pub fn with_len(&self, length: usize) -> Self {
122 let mut cloned = *self;
123 cloned.set_len(length);
124 cloned
125 }
126
127 pub fn push_by(&mut self, by: usize) {
129 self.start += by;
130 self.end += by;
131 }
132
133 pub fn pull_by(&mut self, by: usize) {
135 self.start -= by;
136 self.end -= by;
137 }
138
139 pub fn pushed_by(&self, by: usize) -> Self {
141 let mut clone = *self;
142 clone.start += by;
143 clone.end += by;
144 clone
145 }
146
147 pub fn pulled_by(&self, by: usize) -> Option<Self> {
149 if by > self.start {
150 return None;
151 }
152
153 let mut clone = *self;
154 clone.start -= by;
155 clone.end -= by;
156 Some(clone)
157 }
158}
159
160impl<T: Display + std::fmt::Debug> Span<T> {
162 pub fn get_content_string(&self, source: &[T]) -> String {
164 if let Some(content) = self.try_get_content(source) {
165 content.iter().map(|t| t.to_string()).collect()
166 } else {
167 panic!("Could not get position {self:?} within \"{source:?}\"")
168 }
169 }
170}
171
172impl Span<Token> {
174 pub fn to_char_span(&self, source_document_tokens: &[Token]) -> Span<char> {
180 if self.is_empty() {
181 Span::EMPTY
182 } else {
183 let target_tokens = &source_document_tokens[self.start..self.end];
184 Span::new(
185 target_tokens.first().unwrap().span.start,
186 target_tokens.last().unwrap().span.end,
187 )
188 }
189 }
190}
191
192impl<T> From<Range<usize>> for Span<T> {
193 fn from(value: Range<usize>) -> Self {
195 Self::new(value.start, value.end)
196 }
197}
198
199impl<T> From<Span<T>> for Range<usize> {
200 fn from(value: Span<T>) -> Self {
202 value.start..value.end
203 }
204}
205
206impl<T> IntoIterator for Span<T> {
207 type Item = usize;
208
209 type IntoIter = Range<usize>;
210
211 fn into_iter(self) -> Self::IntoIter {
216 self.start..self.end
217 }
218}
219
220impl<T> Clone for Span<T> {
221 fn clone(&self) -> Self {
223 *self
224 }
225}
226impl<T> Copy for Span<T> {}
227
228#[cfg(test)]
229mod tests {
230 use crate::{
231 Document,
232 expr::{ExprExt, SequenceExpr},
233 };
234
235 use super::Span;
236
237 type UntypedSpan = Span<()>;
238
239 #[test]
240 fn overlaps() {
241 assert!(UntypedSpan::new(0, 5).overlaps_with(UntypedSpan::new(3, 6)));
242 assert!(UntypedSpan::new(0, 5).overlaps_with(UntypedSpan::new(2, 3)));
243 assert!(UntypedSpan::new(0, 5).overlaps_with(UntypedSpan::new(4, 5)));
244 assert!(UntypedSpan::new(0, 5).overlaps_with(UntypedSpan::new(4, 4)));
245
246 assert!(!UntypedSpan::new(0, 3).overlaps_with(UntypedSpan::new(3, 5)));
247 }
248
249 #[test]
250 fn expands_properly() {
251 let mut span = UntypedSpan::new(2, 2);
252
253 span.expand_to_include(1);
254 assert_eq!(span, UntypedSpan::new(1, 2));
255
256 span.expand_to_include(2);
257 assert_eq!(span, UntypedSpan::new(1, 3));
258 }
259
260 #[test]
261 fn to_char_span_converts_correctly() {
262 let doc = Document::new_plain_english_curated("Hello world!");
263
264 let token_span = Span::EMPTY;
266 let converted = token_span.to_char_span(doc.get_tokens());
267 assert!(converted.is_empty());
268
269 let token_span = SequenceExpr::default()
271 .then_any_word()
272 .t_ws()
273 .then_any_word()
274 .iter_matches_in_doc(&doc)
275 .next()
276 .unwrap();
277 let converted = token_span.to_char_span(doc.get_tokens());
278 assert_eq!(
279 converted.get_content_string(doc.get_source()),
280 "Hello world"
281 );
282 }
283}