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 assert!(self.start <= self.end);
78
79 self.start <= idx && idx < self.end
80 }
81
82 pub fn overlaps_with(&self, other: Self) -> bool {
84 (self.start < other.end) && (other.start < self.end)
85 }
86
87 pub fn try_get_content<'a>(&self, source: &'a [T]) -> Option<&'a [T]> {
90 if (self.start > self.end) || (self.start >= source.len()) || (self.end > source.len()) {
91 if self.is_empty() {
92 return Some(&source[0..0]);
93 }
94 return None;
95 }
96
97 Some(&source[self.start..self.end])
98 }
99
100 pub fn expand_to_include(&mut self, target: usize) {
105 if target < self.start {
106 self.start = target;
107 } else if target >= self.end {
108 self.end = target + 1;
109 }
110 }
111
112 pub fn get_content<'a>(&self, source: &'a [T]) -> &'a [T] {
114 match self.try_get_content(source) {
115 Some(v) => v,
116 None => panic!("Failed to get content for span."),
117 }
118 }
119
120 pub fn set_len(&mut self, length: usize) {
122 self.end = self.start + length;
123 }
124
125 pub fn with_len(&self, length: usize) -> Self {
127 let mut cloned = *self;
128 cloned.set_len(length);
129 cloned
130 }
131
132 pub fn push_by(&mut self, by: usize) {
134 self.start += by;
135 self.end += by;
136 }
137
138 pub fn pull_by(&mut self, by: usize) {
140 self.start -= by;
141 self.end -= by;
142 }
143
144 pub fn pushed_by(&self, by: usize) -> Self {
146 let mut clone = *self;
147 clone.start += by;
148 clone.end += by;
149 clone
150 }
151
152 pub fn pulled_by(&self, by: usize) -> Option<Self> {
154 if by > self.start {
155 return None;
156 }
157
158 let mut clone = *self;
159 clone.start -= by;
160 clone.end -= by;
161 Some(clone)
162 }
163
164 pub fn with_offset(&self, by: usize) -> Self {
166 let mut clone = *self;
167 clone.push_by(by);
168 clone
169 }
170}
171
172impl<T: Display + std::fmt::Debug> Span<T> {
174 pub fn get_content_string(&self, source: &[T]) -> String {
176 if let Some(content) = self.try_get_content(source) {
177 content.iter().map(|t| t.to_string()).collect()
178 } else {
179 panic!("Could not get position {self:?} within \"{source:?}\"")
180 }
181 }
182}
183
184impl Span<Token> {
186 pub fn to_char_span(&self, source_document_tokens: &[Token]) -> Span<char> {
192 if self.is_empty() {
193 Span::EMPTY
194 } else {
195 let target_tokens = &source_document_tokens[self.start..self.end];
196 Span::new(
197 target_tokens.first().unwrap().span.start,
198 target_tokens.last().unwrap().span.end,
199 )
200 }
201 }
202}
203
204impl<T> From<Range<usize>> for Span<T> {
205 fn from(value: Range<usize>) -> Self {
207 Self::new(value.start, value.end)
208 }
209}
210
211impl<T> From<Span<T>> for Range<usize> {
212 fn from(value: Span<T>) -> Self {
214 value.start..value.end
215 }
216}
217
218impl<T> IntoIterator for Span<T> {
219 type Item = usize;
220
221 type IntoIter = Range<usize>;
222
223 fn into_iter(self) -> Self::IntoIter {
228 self.start..self.end
229 }
230}
231
232impl<T> Clone for Span<T> {
233 fn clone(&self) -> Self {
235 *self
236 }
237}
238impl<T> Copy for Span<T> {}
239
240#[cfg(test)]
241mod tests {
242 use crate::{
243 Document,
244 expr::{ExprExt, SequenceExpr},
245 };
246
247 use super::Span;
248
249 type UntypedSpan = Span<()>;
250
251 #[test]
252 fn overlaps() {
253 assert!(UntypedSpan::new(0, 5).overlaps_with(UntypedSpan::new(3, 6)));
254 assert!(UntypedSpan::new(0, 5).overlaps_with(UntypedSpan::new(2, 3)));
255 assert!(UntypedSpan::new(0, 5).overlaps_with(UntypedSpan::new(4, 5)));
256 assert!(UntypedSpan::new(0, 5).overlaps_with(UntypedSpan::new(4, 4)));
257
258 assert!(!UntypedSpan::new(0, 3).overlaps_with(UntypedSpan::new(3, 5)));
259 }
260
261 #[test]
262 fn expands_properly() {
263 let mut span = UntypedSpan::new(2, 2);
264
265 span.expand_to_include(1);
266 assert_eq!(span, UntypedSpan::new(1, 2));
267
268 span.expand_to_include(2);
269 assert_eq!(span, UntypedSpan::new(1, 3));
270 }
271
272 #[test]
273 fn to_char_span_converts_correctly() {
274 let doc = Document::new_plain_english_curated("Hello world!");
275
276 let token_span = Span::EMPTY;
278 let converted = token_span.to_char_span(doc.get_tokens());
279 assert!(converted.is_empty());
280
281 let token_span = SequenceExpr::default()
283 .then_any_word()
284 .t_ws()
285 .then_any_word()
286 .iter_matches_in_doc(&doc)
287 .next()
288 .unwrap();
289 let converted = token_span.to_char_span(doc.get_tokens());
290 assert_eq!(
291 converted.get_content_string(doc.get_source()),
292 "Hello world"
293 );
294 }
295}