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, Hash)]
19pub struct Span<T> {
20 pub start: usize,
22 pub end: usize,
27 #[serde(skip)]
28 span_type: PhantomData<T>,
29}
30
31impl<T> Span<T> {
32 pub const ZERO: Self = Self::empty(0);
34
35 pub(crate) const FULL: Self = Self {
37 start: usize::MIN,
38 end: usize::MAX,
39 span_type: PhantomData,
40 };
41
42 pub fn new(start: usize, end: usize) -> Self {
48 if start > end {
49 panic!("{start} > {end}");
50 }
51 Self {
52 start,
53 end,
54 span_type: PhantomData,
55 }
56 }
57
58 pub fn new_with_len(start: usize, len: usize) -> Self {
60 Self {
61 start,
62 end: start + len,
63 span_type: PhantomData,
64 }
65 }
66
67 pub const fn empty(pos: usize) -> Self {
69 Self {
70 start: pos,
71 end: pos,
72 span_type: PhantomData,
73 }
74 }
75
76 pub fn len(&self) -> usize {
78 self.end - self.start
79 }
80
81 pub fn is_empty(&self) -> bool {
85 self.len() == 0
86 }
87
88 pub fn contains(&self, idx: usize) -> bool {
90 self.start <= idx && idx < self.end
91 }
92
93 pub fn overlaps_with(&self, other: Self) -> bool {
95 (self.start < other.end) && (other.start < self.end)
96 }
97
98 pub fn try_get_content<'a>(&self, source: &'a [T]) -> Option<&'a [T]> {
101 if self.is_empty() {
102 Some(&source[0..0])
103 } else {
104 source.get(self.start..self.end)
105 }
106 }
107
108 pub fn expand_to_include(&mut self, target: usize) {
113 if target < self.start {
114 self.start = target;
115 } else if target >= self.end {
116 self.end = target + 1;
117 }
118 }
119
120 pub fn get_content<'a>(&self, source: &'a [T]) -> &'a [T] {
122 match self.try_get_content(source) {
123 Some(v) => v,
124 None => panic!("Failed to get content for span."),
125 }
126 }
127
128 pub fn set_len(&mut self, length: usize) {
130 self.end = self.start + length;
131 }
132
133 pub fn with_len(&self, length: usize) -> Self {
135 let mut cloned = *self;
136 cloned.set_len(length);
137 cloned
138 }
139
140 pub fn push_by(&mut self, by: usize) {
142 self.start += by;
143 self.end += by;
144 }
145
146 pub fn pull_by(&mut self, by: usize) {
148 self.start -= by;
149 self.end -= by;
150 }
151
152 pub fn pushed_by(&self, by: usize) -> Self {
154 let mut clone = *self;
155 clone.start += by;
156 clone.end += by;
157 clone
158 }
159
160 pub fn pulled_by(&self, by: usize) -> Option<Self> {
162 if by > self.start {
163 return None;
164 }
165
166 let mut clone = *self;
167 clone.start -= by;
168 clone.end -= by;
169 Some(clone)
170 }
171}
172
173impl<T: Display + std::fmt::Debug> Span<T> {
175 pub fn get_content_string(&self, source: &[T]) -> String {
177 if let Some(content) = self.try_get_content(source) {
178 content.iter().map(|t| t.to_string()).collect()
179 } else {
180 panic!("Could not get position {self:?} within \"{source:?}\"")
181 }
182 }
183}
184
185impl Span<Token> {
187 pub fn to_char_span(&self, source_document_tokens: &[Token]) -> Span<char> {
193 if self.is_empty() {
194 Span::ZERO
195 } else {
196 let target_tokens = &source_document_tokens[self.start..self.end];
197 Span::new(
198 target_tokens.first().unwrap().span.start,
199 target_tokens.last().unwrap().span.end,
200 )
201 }
202 }
203}
204
205impl<T> From<Range<usize>> for Span<T> {
206 fn from(value: Range<usize>) -> Self {
208 Self::new(value.start, value.end)
209 }
210}
211
212impl<T> From<Span<T>> for Range<usize> {
213 fn from(value: Span<T>) -> Self {
215 value.start..value.end
216 }
217}
218
219impl<T> IntoIterator for Span<T> {
220 type Item = usize;
221
222 type IntoIter = Range<usize>;
223
224 fn into_iter(self) -> Self::IntoIter {
229 self.start..self.end
230 }
231}
232
233impl<T> Clone for Span<T> {
234 fn clone(&self) -> Self {
236 *self
237 }
238}
239impl<T> Copy for Span<T> {}
240
241#[cfg(test)]
242mod tests {
243 use crate::{
244 Document,
245 expr::{ExprExt, SequenceExpr},
246 };
247
248 use super::Span;
249
250 type UntypedSpan = Span<()>;
251
252 #[test]
253 fn overlaps() {
254 assert!(UntypedSpan::new(0, 5).overlaps_with(UntypedSpan::new(3, 6)));
255 assert!(UntypedSpan::new(0, 5).overlaps_with(UntypedSpan::new(2, 3)));
256 assert!(UntypedSpan::new(0, 5).overlaps_with(UntypedSpan::new(4, 5)));
257 assert!(UntypedSpan::new(0, 5).overlaps_with(UntypedSpan::new(4, 4)));
258
259 assert!(!UntypedSpan::new(0, 3).overlaps_with(UntypedSpan::new(3, 5)));
260 }
261
262 #[test]
263 fn expands_properly() {
264 let mut span = UntypedSpan::new(2, 2);
265
266 span.expand_to_include(1);
267 assert_eq!(span, UntypedSpan::new(1, 2));
268
269 span.expand_to_include(2);
270 assert_eq!(span, UntypedSpan::new(1, 3));
271 }
272
273 #[test]
274 fn to_char_span_converts_correctly() {
275 let doc = Document::new_plain_english_curated("Hello world!");
276
277 let token_span = Span::ZERO;
279 let converted = token_span.to_char_span(doc.get_tokens());
280 assert!(converted.is_empty());
281
282 let token_span = SequenceExpr::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}