1use crate::{SourceOffset, Span, ToSpan};
2
3pub trait SourceToken: Copy {
5 type Kind: Copy;
7
8 const EMPTY: Self;
10
11 fn kind(self) -> Self::Kind;
13
14 fn len(self) -> u32;
16
17 fn kind_name(self) -> &'static str;
19
20 fn leading_len(self) -> u32 {
22 0
23 }
24
25 fn trailing_len(self) -> u32 {
27 0
28 }
29
30 fn is_empty(self) -> bool {
32 self.len() == 0
33 }
34}
35
36#[repr(C)]
38#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
39pub struct Cursor<T>(SourceOffset, T);
40
41impl<T> Cursor<T> {
42 #[inline(always)]
44 pub const fn new(offset: SourceOffset, token: T) -> Self {
45 Self(offset, token)
46 }
47
48 #[inline(always)]
50 pub const fn dummy(token: T) -> Self {
51 Self(SourceOffset::DUMMY, token)
52 }
53
54 #[inline(always)]
56 pub const fn token_ref(&self) -> &T {
57 &self.1
58 }
59
60 #[inline(always)]
62 pub const fn offset(&self) -> SourceOffset {
63 self.0
64 }
65
66 #[inline(always)]
68 pub fn with_token<U>(self, token: U) -> Cursor<U> {
69 Cursor(self.0, token)
70 }
71
72 #[inline(always)]
74 pub fn map_token<U>(self, map: impl FnOnce(T) -> U) -> Cursor<U> {
75 Cursor(self.0, map(self.1))
76 }
77}
78
79impl<T: Copy> Cursor<T> {
80 #[inline(always)]
82 pub const fn token(&self) -> T {
83 self.1
84 }
85}
86
87impl<T: SourceToken> Cursor<T> {
88 pub const EMPTY: Self = Self(SourceOffset::ZERO, T::EMPTY);
90
91 #[inline(always)]
93 pub fn kind(&self) -> T::Kind {
94 self.1.kind()
95 }
96
97 #[inline(always)]
99 pub fn end_offset(&self) -> SourceOffset {
100 if self.0 == SourceOffset::DUMMY {
101 return self.0;
102 }
103 SourceOffset(self.0.0 + self.len())
104 }
105
106 #[inline(always)]
108 pub fn len(&self) -> u32 {
109 self.1.len()
110 }
111
112 #[inline(always)]
114 pub fn is_empty(&self) -> bool {
115 self.1.is_empty()
116 }
117
118 #[inline(always)]
120 pub fn span(&self) -> Span {
121 Span::new(self.0, self.end_offset())
122 }
123
124 #[inline(always)]
126 pub fn str_slice<'a>(&self, source: &'a str) -> &'a str {
127 let start = self.0.0 as usize;
128 let end = self.end_offset().0 as usize;
129 debug_assert!(source.len() >= end, "attempted to index out of bounds ({} < {})", source.len(), end);
130 &source[start..end]
131 }
132
133 pub fn value_slice<'a>(&self, source: &'a str) -> &'a str {
135 let start = (self.0.0 + self.1.leading_len()) as usize;
136 let end = (self.0.0 + self.len() - self.1.trailing_len()) as usize;
137 &source[start..end]
138 }
139}
140
141impl<T: SourceToken> ToSpan for Cursor<T> {
142 fn to_span(&self) -> Span {
143 self.span()
144 }
145}
146
147impl<T: SourceToken> From<Cursor<T>> for Span {
148 fn from(cursor: Cursor<T>) -> Self {
149 cursor.span()
150 }
151}
152
153impl<T: SourceToken> PartialEq<Span> for Cursor<T> {
154 fn eq(&self, other: &Span) -> bool {
155 self.span() == *other
156 }
157}
158
159impl<T: PartialEq<char>> PartialEq<char> for Cursor<T> {
160 fn eq(&self, other: &char) -> bool {
161 self.1 == *other
162 }
163}
164
165impl<T: PartialEq<char>> PartialEq<char> for &Cursor<T> {
166 fn eq(&self, other: &char) -> bool {
167 self.token_ref() == other
168 }
169}
170
171#[cfg(feature = "miette")]
172impl<T: SourceToken> From<Cursor<T>> for miette::SourceSpan {
173 fn from(cursor: Cursor<T>) -> Self {
174 cursor.span().into()
175 }
176}
177
178#[cfg(feature = "serde")]
179impl<T: SourceToken> serde::Serialize for Cursor<T> {
180 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
181 where
182 S: serde::Serializer,
183 {
184 use serde::ser::SerializeStruct;
185 if self.is_empty() {
186 return serializer.serialize_none();
187 }
188 let mut state = serializer.serialize_struct("Cursor", 3)?;
189 state.serialize_field("kind", self.1.kind_name())?;
190 state.serialize_field("offset", &self.0)?;
191 state.serialize_field("len", &self.len())?;
192 state.end()
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199
200 #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
201 #[repr(C)]
202 struct Token(u32, u32);
203
204 impl SourceToken for Token {
205 type Kind = u32;
206 const EMPTY: Self = Token(0, 0);
207
208 fn kind(self) -> Self::Kind {
209 self.0
210 }
211
212 fn len(self) -> u32 {
213 self.1
214 }
215
216 fn kind_name(self) -> &'static str {
217 "test"
218 }
219 }
220
221 #[test]
222 fn cursor_preserves_layout_and_span() {
223 let cursor = Cursor::new(SourceOffset(3), Token(1, 4));
224 assert_eq!(size_of::<Cursor<Token>>(), 12);
225 assert_eq!(cursor.span(), Span::new(SourceOffset(3), SourceOffset(7)));
226 }
227
228 #[test]
229 fn dummy_cursor_keeps_dummy_end_offset() {
230 let cursor = Cursor::dummy(Token(1, 4));
231 assert_eq!(cursor.end_offset(), SourceOffset::DUMMY);
232 }
233}