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 #[serde(skip)]
28 span_type: PhantomData<T>,
29}
30
31impl<T> Span<T> {
32 pub const ZERO: Self = Self::empty(0);
34
35 pub fn new(start: usize, end: usize) -> Self {
41 if start > end {
42 panic!("{start} > {end}");
43 }
44 Self {
45 start,
46 end,
47 span_type: PhantomData,
48 }
49 }
50
51 pub fn new_with_len(start: usize, len: usize) -> Self {
53 Self {
54 start,
55 end: start + len,
56 span_type: PhantomData,
57 }
58 }
59
60 pub const fn empty(pos: usize) -> Self {
62 Self {
63 start: pos,
64 end: pos,
65 span_type: PhantomData,
66 }
67 }
68
69 pub fn len(&self) -> usize {
71 self.end - self.start
72 }
73
74 pub fn is_empty(&self) -> bool {
78 self.len() == 0
79 }
80
81 pub fn contains(&self, idx: usize) -> bool {
83 self.start <= idx && idx < self.end
84 }
85
86 pub fn overlaps_with(&self, other: Self) -> bool {
88 (self.start < other.end) && (other.start < self.end)
89 }
90
91 pub fn try_get_content<'a>(&self, source: &'a [T]) -> Option<&'a [T]> {
94 if self.is_empty() {
95 Some(&source[0..0])
96 } else {
97 source.get(self.start..self.end)
98 }
99 }
100
101 pub fn expand_to_include(&mut self, target: usize) {
106 if target < self.start {
107 self.start = target;
108 } else if target >= self.end {
109 self.end = target + 1;
110 }
111 }
112
113 pub fn get_content<'a>(&self, source: &'a [T]) -> &'a [T] {
115 match self.try_get_content(source) {
116 Some(v) => v,
117 None => panic!("Failed to get content for span."),
118 }
119 }
120
121 pub fn set_len(&mut self, length: usize) {
123 self.end = self.start + length;
124 }
125
126 pub fn with_len(&self, length: usize) -> Self {
128 let mut cloned = *self;
129 cloned.set_len(length);
130 cloned
131 }
132
133 pub fn push_by(&mut self, by: usize) {
135 self.start += by;
136 self.end += by;
137 }
138
139 pub fn pull_by(&mut self, by: usize) {
141 self.start -= by;
142 self.end -= by;
143 }
144
145 pub fn pushed_by(&self, by: usize) -> Self {
147 let mut clone = *self;
148 clone.start += by;
149 clone.end += by;
150 clone
151 }
152
153 pub fn pulled_by(&self, by: usize) -> Option<Self> {
155 if by > self.start {
156 return None;
157 }
158
159 let mut clone = *self;
160 clone.start -= by;
161 clone.end -= by;
162 Some(clone)
163 }
164}
165
166impl<T: Display + std::fmt::Debug> Span<T> {
168 pub fn get_content_string(&self, source: &[T]) -> String {
170 if let Some(content) = self.try_get_content(source) {
171 content.iter().map(|t| t.to_string()).collect()
172 } else {
173 panic!("Could not get position {self:?} within \"{source:?}\"")
174 }
175 }
176}
177
178impl Span<Token> {
180 pub fn to_char_span(&self, source_document_tokens: &[Token]) -> Span<char> {
186 if self.is_empty() {
187 Span::ZERO
188 } else {
189 let target_tokens = &source_document_tokens[self.start..self.end];
190 Span::new(
191 target_tokens.first().unwrap().span.start,
192 target_tokens.last().unwrap().span.end,
193 )
194 }
195 }
196}
197
198impl<T> From<Range<usize>> for Span<T> {
199 fn from(value: Range<usize>) -> Self {
201 Self::new(value.start, value.end)
202 }
203}
204
205impl<T> From<Span<T>> for Range<usize> {
206 fn from(value: Span<T>) -> Self {
208 value.start..value.end
209 }
210}
211
212impl<T> IntoIterator for Span<T> {
213 type Item = usize;
214
215 type IntoIter = Range<usize>;
216
217 fn into_iter(self) -> Self::IntoIter {
222 self.start..self.end
223 }
224}
225
226impl<T> Clone for Span<T> {
227 fn clone(&self) -> Self {
229 *self
230 }
231}
232impl<T> Copy for Span<T> {}
233
234#[cfg(test)]
235mod tests {
236 use crate::{
237 Document,
238 expr::{ExprExt, SequenceExpr},
239 };
240
241 use super::Span;
242
243 type UntypedSpan = Span<()>;
244
245 #[test]
246 fn overlaps() {
247 assert!(UntypedSpan::new(0, 5).overlaps_with(UntypedSpan::new(3, 6)));
248 assert!(UntypedSpan::new(0, 5).overlaps_with(UntypedSpan::new(2, 3)));
249 assert!(UntypedSpan::new(0, 5).overlaps_with(UntypedSpan::new(4, 5)));
250 assert!(UntypedSpan::new(0, 5).overlaps_with(UntypedSpan::new(4, 4)));
251
252 assert!(!UntypedSpan::new(0, 3).overlaps_with(UntypedSpan::new(3, 5)));
253 }
254
255 #[test]
256 fn expands_properly() {
257 let mut span = UntypedSpan::new(2, 2);
258
259 span.expand_to_include(1);
260 assert_eq!(span, UntypedSpan::new(1, 2));
261
262 span.expand_to_include(2);
263 assert_eq!(span, UntypedSpan::new(1, 3));
264 }
265
266 #[test]
267 fn to_char_span_converts_correctly() {
268 let doc = Document::new_plain_english_curated("Hello world!");
269
270 let token_span = Span::ZERO;
272 let converted = token_span.to_char_span(doc.get_tokens());
273 assert!(converted.is_empty());
274
275 let token_span = SequenceExpr::default()
277 .then_any_word()
278 .t_ws()
279 .then_any_word()
280 .iter_matches_in_doc(&doc)
281 .next()
282 .unwrap();
283 let converted = token_span.to_char_span(doc.get_tokens());
284 assert_eq!(
285 converted.get_content_string(doc.get_source()),
286 "Hello world"
287 );
288 }
289}