1use crate::shell_error::generic::GenericError;
3use crate::{FromValue, IntoValue, ShellError, Signals, SpanId, Type, Value, record};
4use miette::SourceSpan;
5use serde::{Deserialize, Serialize};
6use std::borrow::Cow;
7use std::{fmt, ops::Deref};
8
9pub trait GetSpan {
10 fn get_span(&self, span_id: SpanId) -> Span;
11}
12
13#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
15pub struct Spanned<T> {
16 pub item: T,
17 pub span: Span,
18}
19
20impl<T> Spanned<T> {
21 pub fn as_ref(&self) -> Spanned<&T> {
23 Spanned {
24 item: &self.item,
25 span: self.span,
26 }
27 }
28
29 pub fn as_mut(&mut self) -> Spanned<&mut T> {
31 Spanned {
32 item: &mut self.item,
33 span: self.span,
34 }
35 }
36
37 pub fn as_deref(&self) -> Spanned<&<T as Deref>::Target>
41 where
42 T: Deref,
43 {
44 Spanned {
45 item: self.item.deref(),
46 span: self.span,
47 }
48 }
49
50 pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Spanned<U> {
52 Spanned {
53 item: f(self.item),
54 span: self.span,
55 }
56 }
57}
58
59impl<T> Spanned<&T>
60where
61 T: ToOwned + ?Sized,
62{
63 pub fn to_owned(&self) -> Spanned<T::Owned> {
65 Spanned {
66 item: self.item.to_owned(),
67 span: self.span,
68 }
69 }
70}
71
72impl<T> Spanned<T>
73where
74 T: AsRef<str>,
75{
76 pub fn as_str(&self) -> Spanned<&str> {
78 Spanned {
79 item: self.item.as_ref(),
80 span: self.span,
81 }
82 }
83}
84
85impl<T, E> Spanned<Result<T, E>> {
86 pub fn transpose(self) -> Result<Spanned<T>, E> {
88 match self {
89 Spanned {
90 item: Ok(item),
91 span,
92 } => Ok(Spanned { item, span }),
93 Spanned {
94 item: Err(err),
95 span: _,
96 } => Err(err),
97 }
98 }
99}
100
101impl<T: fmt::Display> fmt::Display for Spanned<T> {
105 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106 fmt::Display::fmt(&self.item, f)
107 }
108}
109
110impl<T> From<Spanned<T>> for SourceSpan {
111 fn from(value: Spanned<T>) -> Self {
112 value.span.into()
113 }
114}
115
116pub trait IntoSpanned: Sized {
118 fn into_spanned(self, span: Span) -> Spanned<Self>;
130}
131
132impl<T> IntoSpanned for T {
133 fn into_spanned(self, span: Span) -> Spanned<Self> {
134 Spanned { item: self, span }
135 }
136}
137
138#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
142pub struct Span {
143 pub start: usize,
144 pub end: usize,
145}
146
147#[derive(Clone)]
148pub struct ResolvedSpan<'a> {
149 pub file: Cow<'a, str>,
150 pub span: Span,
151}
152
153impl<'a> IntoValue for ResolvedSpan<'a> {
154 fn into_value(self, span: Span) -> Value {
155 let record = record! {
156 "file" => self.file.into_value(span),
157 "start" => Value::int(self.span.start as i64, span),
158 "end" => Value::int(self.span.end as i64, span),
159 };
160 record.into_value(span)
161 }
162}
163
164impl fmt::Debug for Span {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 const TEST_DATA: Span = Span::test_data();
167 const UNKNOWN: Span = Span::unknown();
168
169 match *self {
170 TEST_DATA => write!(f, "Span(TEST)"),
171 UNKNOWN => write!(f, "Span(UNKNOWN)"),
172 Span { start, end } => write!(f, "Span[{start}..{end}]"),
173 }
174 }
175}
176
177impl Span {
178 pub fn new(start: usize, end: usize) -> Self {
179 debug_assert!(
180 end >= start,
181 "Can't create a Span whose end < start, start={start}, end={end}"
182 );
183
184 Self { start, end }
185 }
186
187 pub const fn unknown() -> Self {
188 Self { start: 0, end: 0 }
189 }
190
191 pub const fn test_data() -> Self {
198 Self {
199 start: usize::MAX / 2,
200 end: usize::MAX / 2,
201 }
202 }
203
204 pub fn offset(&self, offset: usize) -> Self {
205 Self::new(self.start - offset, self.end - offset)
206 }
207
208 pub fn len(&self) -> usize {
210 self.end - self.start
211 }
212
213 pub fn is_empty(&self) -> bool {
215 self.start == self.end
216 }
217
218 pub fn subspan(&self, offset_start: usize, offset_end: usize) -> Option<Self> {
223 let len = self.len();
224
225 if offset_start > len || offset_end > len || offset_start > offset_end {
226 None
227 } else {
228 Some(Self::new(
229 self.start + offset_start,
230 self.start + offset_end,
231 ))
232 }
233 }
234
235 pub fn split_at(&self, offset: usize) -> Option<(Self, Self)> {
237 if offset < self.len() {
238 Some((
239 Self::new(self.start, self.start + offset),
240 Self::new(self.start + offset, self.end),
241 ))
242 } else {
243 None
244 }
245 }
246
247 pub fn contains(&self, pos: usize) -> bool {
248 self.start <= pos && pos < self.end
249 }
250
251 pub fn contains_span(&self, span: Self) -> bool {
252 self.start <= span.start && span.end <= self.end && span.end != 0
253 }
254
255 pub const fn point(pos: usize) -> Self {
257 Self {
258 start: pos,
259 end: pos,
260 }
261 }
262
263 pub fn past(&self) -> Self {
265 Self {
266 start: self.end,
267 end: self.end,
268 }
269 }
270
271 pub fn from_row_column(row: usize, col: usize, contents: &str) -> Span {
273 let mut cur_row = 1;
274 let mut cur_col = 1;
275
276 for (offset, curr_byte) in contents.bytes().enumerate() {
277 if curr_byte == b'\n' {
278 cur_row += 1;
279 cur_col = 1;
280 } else if cur_row >= row && cur_col >= col {
281 return Span::point(offset);
282 } else {
283 cur_col += 1;
284 }
285 }
286
287 Span::point(contents.len())
288 }
289
290 pub fn try_from_row_column(
295 row: usize,
296 col: usize,
297 contents: &str,
298 span: &Span,
299 signals: &Signals,
300 ) -> Result<Span, ShellError> {
301 let mut cur_row = 1;
302 let mut cur_col = 1;
303
304 for (offset, curr_byte) in contents.bytes().enumerate() {
305 if offset > 0 && offset % 16384 == 0 {
306 signals.check(span)?;
307 }
308 if curr_byte == b'\n' {
309 cur_row += 1;
310 cur_col = 1;
311 } else if cur_row >= row && cur_col >= col {
312 let end = contents.len().min(offset + 1);
314 return Ok(Span::new(offset, end));
315 } else {
316 cur_col += 1;
317 }
318 }
319
320 Ok(Span::point(contents.len()))
321 }
322
323 pub fn append(self, after: Self) -> Self {
332 debug_assert!(
333 self.start <= after.start && self.end <= after.end,
334 "Can't merge two Spans that are not in order"
335 );
336 Self {
337 start: self.start,
338 end: after.end,
339 }
340 }
341
342 pub fn merge(self, other: Self) -> Self {
348 Self {
349 start: usize::min(self.start, other.start),
350 end: usize::max(self.end, other.end),
351 }
352 }
353
354 pub fn concat(spans: &[Self]) -> Self {
364 debug_assert!(spans.windows(2).all(|spans| {
367 let &[a, b] = spans else {
368 return false;
369 };
370 a.start <= b.start && a.end <= b.end
371 }));
372 Self {
373 start: spans.first().map(|s| s.start).unwrap_or(0),
374 end: spans.last().map(|s| s.end).unwrap_or(0),
375 }
376 }
377
378 pub fn merge_many(spans: impl IntoIterator<Item = Self>) -> Self {
384 spans
385 .into_iter()
386 .reduce(Self::merge)
387 .unwrap_or(Self::unknown())
388 }
389
390 pub fn fallback(&mut self, span: Span) -> Span {
392 let current = *self;
393 if current == Span::unknown() || current == Span::test_data() {
394 *self = span;
395 }
396 *self
397 }
398}
399
400impl IntoValue for Span {
401 fn into_value(self, span: Span) -> Value {
402 let record = record! {
403 "start" => Value::int(self.start as i64, self),
404 "end" => Value::int(self.end as i64, self),
405 };
406 record.into_value(span)
407 }
408}
409
410impl FromValue for Span {
411 fn from_value(value: Value) -> Result<Self, ShellError> {
412 let rec = value.as_record();
413 match rec {
414 Ok(val) => {
415 let Some(pre_start) = val.get("start") else {
416 return Err(ShellError::Generic(GenericError::new(
417 "Unable to parse Span.",
418 "`start` must be an `int`",
419 value.span(),
420 )));
421 };
422 let Some(pre_end) = val.get("end") else {
423 return Err(ShellError::Generic(GenericError::new(
424 "Unable to parse Span.",
425 "`end` must be an `int`",
426 value.span(),
427 )));
428 };
429 let start = pre_start.as_int()? as usize;
430 let end = pre_end.as_int()? as usize;
431 if start <= end {
432 Ok(Self::new(start, end))
433 } else {
434 Err(ShellError::Generic(GenericError::new(
435 "Unable to parse Span.",
436 "`end` must not be less than `start`",
437 value.span(),
438 )))
439 }
440 }
441 _ => Err(ShellError::TypeMismatch {
442 err_message: "Must be a record".into(),
443 span: value.span(),
444 }),
445 }
446 }
447 fn expected_type() -> Type {
448 Type::Record([("start", Type::Int), ("end", Type::Int)].into())
449 }
450}
451
452impl From<Span> for SourceSpan {
453 fn from(s: Span) -> Self {
454 Self::new(s.start.into(), s.end - s.start)
455 }
456}
457
458pub trait ErrSpan {
464 type Result;
465
466 fn err_span(self, span: Span) -> Self::Result;
468}
469
470impl<T, E> ErrSpan for Result<T, E> {
471 type Result = Result<T, Spanned<E>>;
472
473 fn err_span(self, span: Span) -> Self::Result {
474 self.map_err(|err| err.into_spanned(span))
475 }
476}
477
478#[cfg(test)]
479mod tests {
480 use super::*;
481 use crate::Signals;
482 use std::sync::{Arc, atomic::AtomicBool};
483
484 #[test]
488 fn try_from_row_column_first_line() {
489 let input = "hello\nworld\nfoo";
490 let signals = Signals::empty();
491 let result = Span::try_from_row_column(1, 3, input, &Span::unknown(), &signals);
492 assert_eq!(result, Ok(Span::new(2, 3))); }
494
495 #[test]
496 fn try_from_row_column_second_line() {
497 let input = "hello\nworld\nfoo";
498 let signals = Signals::empty();
499 let result = Span::try_from_row_column(2, 1, input, &Span::unknown(), &signals);
500 assert_eq!(result, Ok(Span::new(6, 7))); }
502
503 #[test]
504 fn try_from_row_column_last_char() {
505 let input = "hello\nworld\nfoo";
506 let signals = Signals::empty();
507 let result = Span::try_from_row_column(3, 3, input, &Span::unknown(), &signals);
509 assert_eq!(result, Ok(Span::new(14, 15)));
510 }
511
512 #[test]
513 fn try_from_row_column_beyond_input() {
514 let input = "hi";
515 let signals = Signals::empty();
516 let result = Span::try_from_row_column(10, 1, input, &Span::unknown(), &signals);
518 assert_eq!(result, Ok(Span::new(2, 2))); }
520
521 #[test]
522 fn try_from_row_column_interrupted_triggers_error() {
523 let flag = Arc::new(AtomicBool::new(true)); let signals = Signals::new(flag);
526 let input = "x".repeat(20_000);
527 let result = Span::try_from_row_column(1, 18_000, &input, &Span::unknown(), &signals);
528 assert!(result.is_err());
529 assert!(matches!(result, Err(ShellError::Interrupted { .. })));
530 }
531
532 #[test]
533 fn try_from_row_column_short_input_skips_signal_check() {
534 let flag = Arc::new(AtomicBool::new(true)); let signals = Signals::new(flag);
537 let input = "hello\nworld";
538 let result = Span::try_from_row_column(1, 1, input, &Span::unknown(), &signals);
539 assert_eq!(result, Ok(Span::new(0, 1))); }
542
543 #[test]
544 fn try_from_row_column_not_interrupted() {
545 let flag = Arc::new(AtomicBool::new(false));
546 let signals = Signals::new(flag);
547 let input = "a\nb\nc";
548 let result = Span::try_from_row_column(1, 1, input, &Span::unknown(), &signals);
549 assert_eq!(result, Ok(Span::new(0, 1))); }
551
552 #[test]
553 fn try_from_row_column_start_matches_from_row_column() {
554 let input = "line one\nline two\nline three";
557 let signals = Signals::empty();
558 let expected = Span::from_row_column(2, 6, input);
559 let result = Span::try_from_row_column(2, 6, input, &Span::unknown(), &signals)
560 .expect("should succeed");
561 assert_eq!(result.start, expected.start, "start should match");
562 assert!(
563 result.end > result.start,
564 "end should extend past start for visibility"
565 );
566 }
567}