1mod delim;
2pub(crate) mod fallback;
3mod range;
4
5#[doc(inline)]
6pub use delim::*;
7#[doc(inline)]
8pub use range::*;
9
10use crate::source::Location;
11
12pub trait Spanner {
14 fn span(&self) -> Span;
15}
16
17#[derive(Debug, Copy, Clone)]
19pub enum Span {
20 Compiler(proc_macro::Span),
21 Fallback(fallback::Span),
22}
23
24impl Span {
25 #[inline]
26 pub fn call_site() -> Self {
27 if proc_macro::is_available() {
28 Self::Compiler(proc_macro::Span::call_site())
29 } else {
30 Self::Fallback(fallback::Span::call_site())
31 }
32 }
33
34 #[inline]
35 pub fn mixed_site() -> Self {
36 if proc_macro::is_available() {
37 Self::Compiler(proc_macro::Span::mixed_site())
38 } else {
39 Self::Fallback(fallback::Span::mixed_site())
40 }
41 }
42
43 #[inline]
44 pub fn def_site() -> Self {
45 #[cfg(nightly)]
46 if proc_macro::is_available() {
47 return Self::Compiler(proc_macro::Span::def_site());
48 }
49
50 Self::Fallback(fallback::Span::def_site())
51 }
52
53 pub fn start(&self) -> Location {
54 match self {
55 Self::Compiler(v) => {
56 let lc = v.start();
57
58 #[cfg(nightly)]
59 {
60 Location::new(v.byte_range().start, lc.line(), lc.column())
61 }
62
63 #[cfg(not(nightly))]
64 {
65 Location::new(0, lc.line(), lc.column())
66 }
67 }
68 Self::Fallback(v) => v.start(),
69 }
70 }
71
72 pub fn end(&self) -> Location {
73 match self {
74 Self::Compiler(v) => {
75 let lc = v.end();
76
77 #[cfg(nightly)]
78 {
79 Location::new(v.byte_range().end, lc.line(), lc.column())
80 }
81
82 #[cfg(not(nightly))]
83 {
84 let src = v.source_text().unwrap_or_default();
85 let index = line_column_offset(&src, v.line(), v.column());
86 Location::new(index, lc.line(), lc.column())
87 }
88 }
89 Self::Fallback(v) => v.end(),
90 }
91 }
92
93 pub fn byte_range(&self) -> std::ops::Range<usize> {
94 match self {
95 #[allow(unused)]
96 Self::Compiler(v) => {
97 #[cfg(nightly)]
98 {
99 v.byte_range()
100 }
101
102 #[cfg(not(nightly))]
103 {
104 let start = v.start();
105 let end = v.end();
106 let src = v.source_text().unwrap_or_default();
107 let start = line_column_offset(&src, start.line(), start.column());
108 let end = line_column_offset(&src, end.line(), end.column());
109 start..end
110 }
111 }
112 Self::Fallback(v) => v.byte_range(),
113 }
114 }
115
116 #[inline]
117 pub fn len(&self) -> usize {
118 match self {
119 Self::Compiler(_) => 0,
120 Self::Fallback(v) => v.len(),
121 }
122 }
123
124 #[inline]
125 pub fn is_empty(&self) -> bool {
126 match self {
127 Self::Compiler(_) => true,
128 Self::Fallback(v) => v.is_empty(),
129 }
130 }
131
132 #[inline]
133 pub fn contains(&self, i: usize) -> bool {
134 match self {
135 Self::Compiler(_) => false,
136 Self::Fallback(v) => v.contains(i),
137 }
138 }
139
140 #[inline]
141 pub fn is_subset(&self, other: &Self) -> bool {
142 match (self, other) {
143 (Self::Fallback(a), Self::Fallback(b)) => a.is_subset(b),
144 _ => false,
145 }
146 }
147
148 #[inline]
149 pub fn join(&self, other: Self) -> Self {
150 #[cfg(nightly)]
151 if let (Self::Compiler(a), Self::Compiler(b)) = (self, other) {
152 if let Some(joined) = a.join(b) {
153 return Self::Compiler(joined);
154 }
155 }
156
157 if let (Self::Fallback(a), Self::Fallback(b)) = (self, other) {
158 return Self::Fallback(a.join(b));
159 }
160
161 other
162 }
163
164 #[inline]
168 pub fn split(self, at: usize) -> (Span, Span) {
169 match self {
170 Self::Fallback(s) => {
171 let range = s.byte_range();
172 let mid = (range.start + at) as u32;
173 let head = fallback::Span::new(range.start as u32, mid);
174 let rest = fallback::Span::new(mid, range.end as u32);
175 (Span::Fallback(head), Span::Fallback(rest))
176 }
177 other => (other, other),
178 }
179 }
180}
181
182impl Default for Span {
183 #[inline]
184 fn default() -> Self {
185 Self::call_site()
186 }
187}
188
189impl Eq for Span {}
190
191impl PartialEq for Span {
192 fn eq(&self, other: &Self) -> bool {
193 match (self, other) {
194 (Self::Compiler(a), Self::Compiler(b)) => {
195 a.start().line() == b.start().line()
196 && a.start().column() == b.start().column()
197 && a.end().line() == b.end().line()
198 && a.end().column() == b.end().column()
199 }
200 (Self::Fallback(a), Self::Fallback(b)) => a == b,
201 _ => false,
202 }
203 }
204}
205
206impl Ord for Span {
207 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
208 match (self, other) {
209 (Self::Compiler(a), Self::Compiler(b)) => match a.start().line().cmp(&b.start().line()) {
210 std::cmp::Ordering::Equal => match a.start().column().cmp(&b.start().column()) {
211 std::cmp::Ordering::Equal => match a.end().line().cmp(&b.end().line()) {
212 std::cmp::Ordering::Equal => a.end().column().cmp(&b.end().column()),
213 ord => ord,
214 },
215 ord => ord,
216 },
217 ord => ord,
218 },
219 (Self::Fallback(a), Self::Fallback(b)) => a.cmp(b),
220 (Self::Fallback(_), Self::Compiler(_)) => std::cmp::Ordering::Less,
221 (Self::Compiler(_), Self::Fallback(_)) => std::cmp::Ordering::Greater,
222 }
223 }
224}
225
226impl PartialOrd for Span {
227 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
228 Some(self.cmp(other))
229 }
230}
231
232impl std::hash::Hash for Span {
233 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
234 match self {
235 #[allow(unused)]
236 Self::Compiler(s) => {
237 #[cfg(nightly)]
238 {
239 let range = s.byte_range();
240 range.start.hash(state);
241 range.end.hash(state);
242 }
243
244 #[cfg(not(nightly))]
245 {
246 let start = s.start();
247 let end = s.end();
248 let src = s.source_text().unwrap_or_default();
249
250 line_column_offset(&src, start.line(), start.column()).hash(state);
251 line_column_offset(&src, end.line(), end.column()).hash(state);
252 }
253 }
254 Self::Fallback(s) => s.hash(state),
255 }
256 }
257}
258
259impl From<fallback::Span> for Span {
260 #[inline]
261 fn from(value: fallback::Span) -> Self {
262 Self::Fallback(value)
263 }
264}
265
266#[cfg(feature = "serde")]
267impl serde::Serialize for Span {
268 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
269 where
270 S: serde::Serializer,
271 {
272 use serde::ser::SerializeStruct;
273
274 match self {
275 Self::Fallback(v) => v.serialize(s),
276 #[allow(unused)]
277 Self::Compiler(v) => {
278 let mut o = s.serialize_struct("Span", 2)?;
279
280 #[cfg(nightly)]
281 {
282 o.serialize_field("start", &v.byte_range().start)?;
283 o.serialize_field("end", &v.byte_range().end)?;
284 }
285
286 #[cfg(not(nightly))]
287 {
288 let start = v.start();
289 let end = v.end();
290 let src = v.source_text().unwrap_or_default();
291
292 o.serialize_field("start", &line_column_offset(&src, start.line(), start.column()))?;
293 o.serialize_field("end", &line_column_offset(&src, end.line(), end.column()))?;
294 }
295
296 o.end()
297 }
298 }
299 }
300}
301
302pub fn line_column_offset(src: &str, ln: usize, col: usize) -> usize {
303 let mut curr = 0;
304
305 for (i, line) in src.lines().enumerate() {
306 if i + 1 == ln {
307 let offset = line.char_indices().nth(col).map(|(i, _)| i).unwrap_or(line.len());
308 return curr + offset;
309 }
310
311 curr += line.len() + 1;
312 }
313
314 0
315}