1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub struct SourceSpan {
11 pub start: usize,
13 pub end: usize,
15 pub start_line: u32,
17 pub end_line: u32,
19 pub start_col: u32,
21}
22
23impl SourceSpan {
24 pub fn new(start: usize, end: usize, start_line: u32, end_line: u32, start_col: u32) -> Self {
26 SourceSpan {
27 start,
28 end,
29 start_line,
30 end_line,
31 start_col,
32 }
33 }
34
35 pub fn len(&self) -> usize {
37 self.end.saturating_sub(self.start)
38 }
39
40 pub fn is_empty(&self) -> bool {
42 self.start >= self.end
43 }
44
45 pub fn contains(&self, other: &SourceSpan) -> bool {
47 self.start <= other.start && other.end <= self.end
48 }
49
50 pub fn overlaps(&self, other: &SourceSpan) -> bool {
55 if self.is_empty() || other.is_empty() {
57 return false;
58 }
59 self.start < other.end && other.start < self.end
60 }
61}
62
63impl From<swc_common::Span> for SourceSpan {
65 fn from(span: swc_common::Span) -> Self {
66 SourceSpan {
69 start: span.lo.0 as usize,
70 end: span.hi.0 as usize,
71 start_line: 0, end_line: 0, start_col: 0, }
75 }
76}
77
78pub fn span_with_location(
80 span: swc_common::Span,
81 source_map: &swc_common::SourceMap,
82) -> SourceSpan {
83 let start_loc = source_map.lookup_char_pos(span.lo);
84 let end_loc = source_map.lookup_char_pos(span.hi);
85 SourceSpan {
86 start: span.lo.0 as usize,
87 end: span.hi.0 as usize,
88 start_line: start_loc.line as u32,
89 end_line: end_loc.line as u32,
90 start_col: start_loc.col.0 as u32,
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[test]
99 fn test_new() {
100 let span = SourceSpan::new(10, 20, 1, 3, 5);
101 assert_eq!(span.start, 10);
102 assert_eq!(span.end, 20);
103 assert_eq!(span.start_line, 1);
104 assert_eq!(span.end_line, 3);
105 assert_eq!(span.start_col, 5);
106 }
107
108 #[test]
109 fn test_len() {
110 let span = SourceSpan::new(10, 20, 1, 3, 5);
111 assert_eq!(span.len(), 10);
112
113 let empty_span = SourceSpan::new(10, 10, 1, 1, 5);
114 assert_eq!(empty_span.len(), 0);
115 }
116
117 #[test]
118 fn test_is_empty() {
119 let span = SourceSpan::new(10, 20, 1, 3, 5);
120 assert!(!span.is_empty());
121
122 let empty_span = SourceSpan::new(10, 10, 1, 1, 5);
123 assert!(empty_span.is_empty());
124
125 let backwards_span = SourceSpan::new(20, 10, 1, 1, 5);
126 assert!(backwards_span.is_empty());
127 }
128
129 #[test]
130 fn test_contains() {
131 let outer = SourceSpan::new(10, 30, 1, 5, 5);
132 let inner = SourceSpan::new(15, 25, 2, 4, 10);
133 let outside = SourceSpan::new(5, 15, 1, 2, 0);
134
135 assert!(outer.contains(&inner));
136 assert!(!inner.contains(&outer));
137 assert!(!outer.contains(&outside));
138
139 assert!(outer.contains(&outer));
141 }
142
143 #[test]
144 fn test_overlaps() {
145 let span1 = SourceSpan::new(10, 20, 1, 3, 5);
146 let span2 = SourceSpan::new(15, 25, 2, 4, 10);
147 let span3 = SourceSpan::new(25, 30, 4, 5, 20);
148
149 assert!(span1.overlaps(&span2));
150 assert!(span2.overlaps(&span1));
151 assert!(!span1.overlaps(&span3));
152 assert!(!span3.overlaps(&span1));
153
154 assert!(span1.overlaps(&span1));
156 }
157
158 #[test]
159 fn test_edge_cases() {
160 let span1 = SourceSpan::new(10, 20, 1, 3, 5);
162 let span2 = SourceSpan::new(20, 30, 3, 5, 15);
163 assert!(!span1.overlaps(&span2));
164
165 let zero_span = SourceSpan::new(15, 15, 2, 2, 10);
167 assert!(span1.contains(&zero_span));
168 assert!(!zero_span.overlaps(&span1)); }
170}