Skip to main content

moxy_token/span/
mod.rs

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
12/// Provides the source span occupied by a value.
13pub trait Spanner {
14    fn span(&self) -> Span;
15}
16
17/// A compiler or fallback source span.
18#[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    /// Split a span at `head_len` characters from its start, returning
165    /// `(head_span, rest_span)`. Only `Fallback` spans carry offsets we can split;
166    /// for compiler spans we reuse the whole span for both halves.
167    #[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}