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