1use crate::{AssociatedWhitespaceRules, CommentStyle, Kind, KindSet, QuoteStyle, SourceOffset, Span, ToSpan, Token};
2
3#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
5pub struct Cursor(SourceOffset, Token);
6
7impl Cursor {
8 pub const DUMMY_SITE_NUMBER_ZERO: Self = Self(SourceOffset::DUMMY, Token::NUMBER_ZERO);
9 pub const EMPTY: Self = Self(SourceOffset::ZERO, Token::EMPTY);
10
11 #[inline(always)]
12 pub const fn new(offset: SourceOffset, token: Token) -> Self {
13 Self(offset, token)
14 }
15
16 #[inline(always)]
17 pub const fn dummy(token: Token) -> Self {
18 Self(SourceOffset::DUMMY, token)
19 }
20
21 #[inline(always)]
22 pub const fn token(&self) -> Token {
23 self.1
24 }
25
26 #[inline(always)]
27 pub const fn offset(&self) -> SourceOffset {
28 self.0
29 }
30
31 #[inline(always)]
32 pub fn end_offset(&self) -> SourceOffset {
33 if self.offset() == SourceOffset::DUMMY {
34 return self.offset();
35 }
36 SourceOffset(self.offset().0 + self.len())
37 }
38
39 #[inline(always)]
40 pub const fn is_empty(&self) -> bool {
41 self.token().is_empty()
42 }
43
44 #[inline(always)]
45 pub const fn len(&self) -> u32 {
46 self.token().len()
47 }
48
49 #[inline(always)]
50 pub fn span(&self) -> Span {
51 Span::new(self.offset(), self.end_offset())
52 }
53
54 #[inline(always)]
55 pub fn with_bad_flag(&self) -> Self {
56 Self(self.0, self.1.with_bad_flag())
57 }
58
59 #[inline(always)]
60 pub fn is_bad(&self) -> bool {
61 self.1.is_bad()
62 }
63
64 #[inline(always)]
65 pub fn str_slice<'a>(&self, str: &'a str) -> &'a str {
66 debug_assert!(
67 str.len() >= (self.end_offset().0 as usize),
68 "attempted to index out of bounds ({} < {})",
69 str.len(),
70 self.end_offset().0
71 );
72 &str[(self.offset().0 as usize)..(self.end_offset().0 as usize)]
73 }
74
75 pub fn with_quotes(&self, quote_style: QuoteStyle) -> Self {
76 if *self == quote_style || *self != Kind::String {
77 return *self;
78 }
79 Self::new(self.offset(), self.token().with_quotes(quote_style))
80 }
81
82 pub fn with_associated_whitespace(&self, rules: AssociatedWhitespaceRules) -> Self {
83 debug_assert!(self.1 == KindSet::DELIM_LIKE);
84 if self.1.associated_whitespace().to_bits() == rules.to_bits() {
85 return *self;
86 }
87 Self::new(self.offset(), self.token().with_associated_whitespace(rules))
88 }
89
90 pub fn with_sign_required(&self) -> Self {
95 debug_assert!(self.1 == Kind::Number);
96 Self::new(self.offset(), self.token().with_sign_required())
97 }
98
99 #[inline]
100 pub fn atom_bits(&self) -> u32 {
101 self.1.atom_bits()
102 }
103}
104
105impl From<Cursor> for Token {
106 fn from(cursor: Cursor) -> Self {
107 cursor.token()
108 }
109}
110
111impl PartialEq<Token> for Cursor {
112 fn eq(&self, other: &Token) -> bool {
113 self.1 == *other
114 }
115}
116
117impl ToSpan for Cursor {
118 fn to_span(&self) -> Span {
119 self.span()
120 }
121}
122
123impl From<Cursor> for Span {
124 fn from(cursor: Cursor) -> Self {
125 cursor.span()
126 }
127}
128
129impl PartialEq<Span> for Cursor {
130 fn eq(&self, other: &Span) -> bool {
131 self.span() == *other
132 }
133}
134
135impl From<Cursor> for Kind {
136 fn from(cursor: Cursor) -> Self {
137 cursor.token().kind()
138 }
139}
140
141impl PartialEq<Kind> for Cursor {
142 fn eq(&self, other: &Kind) -> bool {
143 self.1 == *other
144 }
145}
146
147impl PartialEq<CommentStyle> for Cursor {
148 fn eq(&self, other: &CommentStyle) -> bool {
149 self.1 == *other
150 }
151}
152
153impl From<Cursor> for KindSet {
154 fn from(cursor: Cursor) -> Self {
155 cursor.token().into()
156 }
157}
158
159impl PartialEq<KindSet> for Cursor {
160 fn eq(&self, other: &KindSet) -> bool {
161 self.1 == *other
162 }
163}
164
165impl From<Cursor> for QuoteStyle {
166 fn from(cursor: Cursor) -> Self {
167 cursor.token().into()
168 }
169}
170
171impl PartialEq<QuoteStyle> for Cursor {
172 fn eq(&self, other: &QuoteStyle) -> bool {
173 self.1 == *other
174 }
175}
176
177impl PartialEq<AssociatedWhitespaceRules> for Cursor {
178 fn eq(&self, other: &AssociatedWhitespaceRules) -> bool {
179 self.1 == *other
180 }
181}
182
183impl PartialEq<char> for Cursor {
184 fn eq(&self, other: &char) -> bool {
185 self.1 == *other
186 }
187}
188
189impl PartialEq<CommentStyle> for &Cursor {
190 fn eq(&self, other: &CommentStyle) -> bool {
191 self.1 == *other
192 }
193}
194
195impl PartialEq<Kind> for &Cursor {
196 fn eq(&self, other: &Kind) -> bool {
197 self.1 == *other
198 }
199}
200
201impl PartialEq<KindSet> for &Cursor {
202 fn eq(&self, other: &KindSet) -> bool {
203 self.1 == *other
204 }
205}
206
207impl PartialEq<QuoteStyle> for &Cursor {
208 fn eq(&self, other: &QuoteStyle) -> bool {
209 self.1 == *other
210 }
211}
212
213impl PartialEq<char> for &Cursor {
214 fn eq(&self, other: &char) -> bool {
215 self.1 == *other
216 }
217}
218
219#[cfg(feature = "miette")]
220impl From<Cursor> for miette::SourceSpan {
221 fn from(val: Cursor) -> Self {
222 let span = val.span();
223 span.into()
224 }
225}
226
227#[cfg(feature = "serde")]
228impl serde::ser::Serialize for Cursor {
229 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
230 where
231 S: serde::ser::Serializer,
232 {
233 use serde::ser::SerializeStruct;
234 if self.token() == Token::EMPTY {
235 return serializer.serialize_none();
236 }
237 let mut state = serializer.serialize_struct("Cursor", 3)?;
238 state.serialize_field("kind", self.token().kind().as_str())?;
239 state.serialize_field("offset", &self.offset())?;
240 state.serialize_field("len", &self.token().len())?;
241 state.end()
242 }
243}
244
245#[test]
246fn size_test() {
247 assert_eq!(::std::mem::size_of::<Cursor>(), 12);
248}