1use std::{cmp::Ordering, fmt, hash::Hash, ops::Range};
2
3#[derive(Clone, Copy, PartialEq, Eq, Hash)]
7pub struct Span {
8 start: usize,
9 end: usize,
10}
11
12impl Span {
13 pub fn new(start: usize, end: usize) -> Self {
15 Span { start, end }
16 }
17
18 pub fn join(self, other: impl std::borrow::Borrow<Span>) -> Self {
20 let other = other.borrow();
21 Self {
22 start: self.start.min(other.start),
23 end: self.end.max(other.end),
24 }
25 }
26
27 pub fn start(&self) -> usize {
29 self.start
30 }
31
32 pub fn end(&self) -> usize {
34 self.end
35 }
36
37 pub fn range(&self) -> Range<usize> {
39 self.start..self.end
40 }
41
42 pub fn source_text(self, full_source: &str) -> &str {
44 let start = self.start.min(full_source.len());
45 let end = self.end.min(full_source.len());
46 &full_source[start..end]
47 }
48
49 pub fn start_line_col(self, source: &str) -> (usize, usize) {
51 let offset = self.start.min(source.len());
52 let mut line = 1;
53 let mut col = 1;
54 for (i, ch) in source.char_indices() {
55 if i >= offset {
56 break;
57 }
58 if ch == '\n' {
59 line += 1;
60 col = 1;
61 } else {
62 col += 1;
63 }
64 }
65 (line, col)
66 }
67
68 pub fn call_site() -> Self {
70 Span { start: 0, end: 0 }
71 }
72}
73
74impl PartialOrd for Span {
75 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
76 Some(self.cmp(other))
77 }
78}
79
80impl Ord for Span {
81 fn cmp(&self, other: &Self) -> Ordering {
82 match self.start.cmp(&other.start) {
83 Ordering::Equal => self.end.cmp(&other.end),
84 ord => ord,
85 }
86 }
87}
88
89impl fmt::Debug for Span {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 write!(f, "Span({}..{})", self.start, self.end)
92 }
93}
94
95impl Default for Span {
96 fn default() -> Self {
97 Self::call_site()
98 }
99}