1use crate::shell_error::generic::GenericError;
3use crate::{FromValue, IntoValue, ShellError, Signals, SpanId, 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 fn past(&self) -> Self {
257 Self {
258 start: self.end,
259 end: self.end,
260 }
261 }
262
263 pub fn from_row_column(row: usize, col: usize, contents: &str) -> Span {
265 let mut cur_row = 1;
266 let mut cur_col = 1;
267
268 for (offset, curr_byte) in contents.bytes().enumerate() {
269 if curr_byte == b'\n' {
270 cur_row += 1;
271 cur_col = 1;
272 } else if cur_row >= row && cur_col >= col {
273 return Span::new(offset, offset);
274 } else {
275 cur_col += 1;
276 }
277 }
278
279 Self {
280 start: contents.len(),
281 end: contents.len(),
282 }
283 }
284
285 pub fn try_from_row_column(
290 row: usize,
291 col: usize,
292 contents: &str,
293 span: &Span,
294 signals: &Signals,
295 ) -> Result<Span, ShellError> {
296 let mut cur_row = 1;
297 let mut cur_col = 1;
298
299 for (offset, curr_byte) in contents.bytes().enumerate() {
300 if offset > 0 && offset % 16384 == 0 {
301 signals.check(span)?;
302 }
303 if curr_byte == b'\n' {
304 cur_row += 1;
305 cur_col = 1;
306 } else if cur_row >= row && cur_col >= col {
307 let end = contents.len().min(offset + 1);
309 return Ok(Span::new(offset, end));
310 } else {
311 cur_col += 1;
312 }
313 }
314
315 Ok(Span::new(contents.len(), contents.len()))
316 }
317
318 pub fn append(self, after: Self) -> Self {
327 debug_assert!(
328 self.start <= after.start && self.end <= after.end,
329 "Can't merge two Spans that are not in order"
330 );
331 Self {
332 start: self.start,
333 end: after.end,
334 }
335 }
336
337 pub fn merge(self, other: Self) -> Self {
343 Self {
344 start: usize::min(self.start, other.start),
345 end: usize::max(self.end, other.end),
346 }
347 }
348
349 pub fn concat(spans: &[Self]) -> Self {
359 debug_assert!(spans.windows(2).all(|spans| {
362 let &[a, b] = spans else {
363 return false;
364 };
365 a.start <= b.start && a.end <= b.end
366 }));
367 Self {
368 start: spans.first().map(|s| s.start).unwrap_or(0),
369 end: spans.last().map(|s| s.end).unwrap_or(0),
370 }
371 }
372
373 pub fn merge_many(spans: impl IntoIterator<Item = Self>) -> Self {
379 spans
380 .into_iter()
381 .reduce(Self::merge)
382 .unwrap_or(Self::unknown())
383 }
384}
385
386impl IntoValue for Span {
387 fn into_value(self, span: Span) -> Value {
388 let record = record! {
389 "start" => Value::int(self.start as i64, self),
390 "end" => Value::int(self.end as i64, self),
391 };
392 record.into_value(span)
393 }
394}
395
396impl FromValue for Span {
397 fn from_value(value: Value) -> Result<Self, ShellError> {
398 let rec = value.as_record();
399 match rec {
400 Ok(val) => {
401 let Some(pre_start) = val.get("start") else {
402 return Err(ShellError::Generic(GenericError::new(
403 "Unable to parse Span.",
404 "`start` must be an `int`",
405 value.span(),
406 )));
407 };
408 let Some(pre_end) = val.get("end") else {
409 return Err(ShellError::Generic(GenericError::new(
410 "Unable to parse Span.",
411 "`end` must be an `int`",
412 value.span(),
413 )));
414 };
415 let start = pre_start.as_int()? as usize;
416 let end = pre_end.as_int()? as usize;
417 if start <= end {
418 Ok(Self::new(start, end))
419 } else {
420 Err(ShellError::Generic(GenericError::new(
421 "Unable to parse Span.",
422 "`end` must not be less than `start`",
423 value.span(),
424 )))
425 }
426 }
427 _ => Err(ShellError::TypeMismatch {
428 err_message: "Must be a record".into(),
429 span: value.span(),
430 }),
431 }
432 }
433}
434
435impl From<Span> for SourceSpan {
436 fn from(s: Span) -> Self {
437 Self::new(s.start.into(), s.end - s.start)
438 }
439}
440
441pub trait ErrSpan {
447 type Result;
448
449 fn err_span(self, span: Span) -> Self::Result;
451}
452
453impl<T, E> ErrSpan for Result<T, E> {
454 type Result = Result<T, Spanned<E>>;
455
456 fn err_span(self, span: Span) -> Self::Result {
457 self.map_err(|err| err.into_spanned(span))
458 }
459}
460
461#[cfg(test)]
462mod tests {
463 use super::*;
464 use crate::Signals;
465 use std::sync::{Arc, atomic::AtomicBool};
466
467 #[test]
471 fn try_from_row_column_first_line() {
472 let input = "hello\nworld\nfoo";
473 let signals = Signals::empty();
474 let result = Span::try_from_row_column(1, 3, input, &Span::unknown(), &signals);
475 assert_eq!(result, Ok(Span::new(2, 3))); }
477
478 #[test]
479 fn try_from_row_column_second_line() {
480 let input = "hello\nworld\nfoo";
481 let signals = Signals::empty();
482 let result = Span::try_from_row_column(2, 1, input, &Span::unknown(), &signals);
483 assert_eq!(result, Ok(Span::new(6, 7))); }
485
486 #[test]
487 fn try_from_row_column_last_char() {
488 let input = "hello\nworld\nfoo";
489 let signals = Signals::empty();
490 let result = Span::try_from_row_column(3, 3, input, &Span::unknown(), &signals);
492 assert_eq!(result, Ok(Span::new(14, 15)));
493 }
494
495 #[test]
496 fn try_from_row_column_beyond_input() {
497 let input = "hi";
498 let signals = Signals::empty();
499 let result = Span::try_from_row_column(10, 1, input, &Span::unknown(), &signals);
501 assert_eq!(result, Ok(Span::new(2, 2))); }
503
504 #[test]
505 fn try_from_row_column_interrupted_triggers_error() {
506 let flag = Arc::new(AtomicBool::new(true)); let signals = Signals::new(flag);
509 let input = "x".repeat(20_000);
510 let result = Span::try_from_row_column(1, 18_000, &input, &Span::unknown(), &signals);
511 assert!(result.is_err());
512 assert!(matches!(result, Err(ShellError::Interrupted { .. })));
513 }
514
515 #[test]
516 fn try_from_row_column_short_input_skips_signal_check() {
517 let flag = Arc::new(AtomicBool::new(true)); let signals = Signals::new(flag);
520 let input = "hello\nworld";
521 let result = Span::try_from_row_column(1, 1, input, &Span::unknown(), &signals);
522 assert_eq!(result, Ok(Span::new(0, 1))); }
525
526 #[test]
527 fn try_from_row_column_not_interrupted() {
528 let flag = Arc::new(AtomicBool::new(false));
529 let signals = Signals::new(flag);
530 let input = "a\nb\nc";
531 let result = Span::try_from_row_column(1, 1, input, &Span::unknown(), &signals);
532 assert_eq!(result, Ok(Span::new(0, 1))); }
534
535 #[test]
536 fn try_from_row_column_start_matches_from_row_column() {
537 let input = "line one\nline two\nline three";
540 let signals = Signals::empty();
541 let expected = Span::from_row_column(2, 6, input);
542 let result = Span::try_from_row_column(2, 6, input, &Span::unknown(), &signals)
543 .expect("should succeed");
544 assert_eq!(result.start, expected.start, "start should match");
545 assert!(
546 result.end > result.start,
547 "end should extend past start for visibility"
548 );
549 }
550}