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