1use core::{iter::once, mem::take};
2
3use crate::{
4 puncts, puncts_spanned, ParseIter, ParseIterExt as _, SetSpan as _,
5 TokenKind,
6};
7use proc_macro::{
8 Delimiter, Group, Ident, Literal, Punct,
9 Spacing::{self, *},
10 Span, TokenStream, TokenTree,
11};
12
13pub trait TokenStreamExt
14 : Default
15 + Extend<TokenTree>
16 + Extend<TokenStream>
17 + IntoIterator<Item = TokenTree>
18 + Sized
19{
20 fn push(&mut self, tt: TokenTree) -> &mut Self {
22 self.extend(once(tt));
23 self
24 }
25
26 fn add(&mut self, stream: TokenStream) -> &mut Self {
28 self.extend(once(stream));
29 self
30 }
31
32 #[must_use]
34 fn take(&mut self) -> Self {
35 take(self)
36 }
37
38 fn grouped(self, delimiter: Delimiter) -> Group;
40
41 fn grouped_paren(self) -> Group {
42 self.grouped(Delimiter::Parenthesis)
43 }
44
45 fn grouped_brace(self) -> Group {
46 self.grouped(Delimiter::Brace)
47 }
48
49 fn grouped_bracket(self) -> Group {
50 self.grouped(Delimiter::Bracket)
51 }
52
53 fn grouped_none(self) -> Group {
54 self.grouped(Delimiter::None)
55 }
56
57 fn split_puncts(self, puncts: impl AsRef<[u8]>) -> Option<(
61 Self,
62 ParseIter<Self::IntoIter>,
63 )>;
64}
65impl TokenStreamExt for TokenStream {
66 fn grouped(self, delimiter: Delimiter) -> Group {
67 Group::new(delimiter, self)
68 }
69
70 fn split_puncts(self, puncts: impl AsRef<[u8]>) -> Option<(
71 Self,
72 ParseIter<Self::IntoIter>,
73 )>
74 {
75 let mut iter = self.parse_iter();
76 Some((iter.split_puncts(puncts)?, iter))
77 }
78
79}
80
81pub trait WalkExt
83 : IntoIterator<Item = TokenTree>
84 + FromIterator<TokenTree>
85{
86 #[must_use]
90 fn walk<F>(self, mut f: F) -> Self
91 where F: FnMut(TokenTree) -> TokenTree
92 {
93 fn walk_impl<I, F>(this: I, f: &mut F) -> I
94 where I: IntoIterator<Item = TokenTree> + FromIterator<TokenTree>,
95 F: FnMut(TokenTree) -> TokenTree
96 {
97 this.into_iter()
98 .map(|tt| {
99 let tt = match tt {
100 TokenTree::Group(g) => {
101 walk_impl(g.stream(), &mut *f)
102 .grouped(g.delimiter())
103 .set_spaned(g.span())
104 .into()
105 },
106 _ => tt,
107 };
108 f(tt)
109 })
110 .collect()
111 }
112 walk_impl(self, &mut f)
113 }
114}
115impl<I: IntoIterator<Item = TokenTree> + FromIterator<TokenTree>> WalkExt for I { }
116
117pub trait TokenTreeExt: Into<TokenTree> + Sized {
118 fn as_ident(&self) -> Option<&Ident> { None }
119 fn as_punct(&self) -> Option<&Punct> { None }
120 fn as_group(&self) -> Option<&Group> { None }
121 fn as_literal(&self) -> Option<&Literal> { None }
122 fn into_ident(self) -> Result<Ident, Self> { Err(self) }
123 fn into_punct(self) -> Result<Punct, Self> { Err(self) }
124 fn into_group(self) -> Result<Group, Self> { Err(self) }
125 fn into_literal(self) -> Result<Literal, Self> { Err(self) }
126
127 fn to_ident(&self) -> Result<&Ident, &Self> {
128 self.as_ident().ok_or(self)
129 }
130
131 fn to_punct(&self) -> Result<&Punct, &Self> {
132 self.as_punct().ok_or(self)
133 }
134
135 fn to_group(&self) -> Result<&Group, &Self> {
136 self.as_group().ok_or(self)
137 }
138
139 fn to_literal(&self) -> Result<&Literal, &Self> {
140 self.as_literal().ok_or(self)
141 }
142
143 fn is_ident(&self) -> bool {
144 self.as_ident().is_some()
145 }
146
147 fn is_punct(&self) -> bool {
148 self.as_punct().is_some()
149 }
150
151 fn is_group(&self) -> bool {
152 self.as_group().is_some()
153 }
154
155 fn is_literal(&self) -> bool {
156 self.as_literal().is_some()
157 }
158
159 fn kind(&self) -> TokenKind {
160 if self.is_literal() {
161 TokenKind::Literal
162 } else if self.is_punct() {
163 TokenKind::Punct
164 } else if self.is_group() {
165 TokenKind::Group
166 } else if self.is_ident() {
167 TokenKind::Ident
168 } else {
169 unimplemented!()
170 }
171 }
172
173 fn is_keyword(&self, keyword: &str) -> bool {
177 self.as_ident().is_some_and(|i| i.to_string() == keyword)
178 }
179
180 fn is_punch(&self, ch: char) -> bool {
184 self.as_punct().is_some_and(|p| p.as_char() == ch)
185 }
186
187 fn is_solid_group(&self) -> bool {
191 self.as_group().is_some_and(|g| g.is_solid_group())
192 }
193
194 fn is_delimiter(&self, delimiter: Delimiter) -> bool {
198 self.as_group().is_some_and(|g| g.is_delimiter(delimiter))
199 }
200
201 fn is_joint(&self) -> bool {
205 self.as_punct().is_some_and(|p| p.spacing() == Joint)
206 }
207
208 fn as_punct_char(&self) -> Option<char> {
209 self.as_punct().map(|p| p.as_char())
210 }
211
212 fn tt(self) -> TokenTree {
216 self.into()
217 }
218
219 fn unit_stream(self) -> TokenStream {
221 self.tt().into()
222 }
223}
224impl TokenTreeExt for TokenTree {
225 fn as_ident(&self) -> Option<&Ident> {
226 match self {
227 TokenTree::Ident(i) => Some(i),
228 _ => None,
229 }
230 }
231
232 fn as_punct(&self) -> Option<&Punct> {
233 match self {
234 TokenTree::Punct(i) => Some(i),
235 _ => None,
236 }
237 }
238
239 fn as_group(&self) -> Option<&Group> {
240 match self {
241 TokenTree::Group(i) => Some(i),
242 _ => None,
243 }
244 }
245
246 fn as_literal(&self) -> Option<&Literal> {
247 match self {
248 TokenTree::Literal(i) => Some(i),
249 _ => None,
250 }
251 }
252
253 fn into_ident(self) -> Result<Ident, Self> {
254 match self {
255 TokenTree::Ident(i) => Ok(i),
256 _ => Err(self),
257 }
258 }
259
260 fn into_punct(self) -> Result<Punct, Self> {
261 match self {
262 TokenTree::Punct(i) => Ok(i),
263 _ => Err(self),
264 }
265 }
266
267 fn into_group(self) -> Result<Group, Self> {
268 match self {
269 TokenTree::Group(i) => Ok(i),
270 _ => Err(self),
271 }
272 }
273
274 fn into_literal(self) -> Result<Literal, Self> {
275 match self {
276 TokenTree::Literal(i) => Ok(i),
277 _ => Err(self),
278 }
279 }
280
281 fn kind(&self) -> TokenKind {
282 self.into()
283 }
284}
285macro_rules! impl_token_tree_ext {
286 ($as:ident, $into:ident, $ty:ident) => {
287 impl TokenTreeExt for $ty {
288 fn $as(&self) -> Option<&$ty> {
289 Some(self)
290 }
291 fn $into(self) -> Result<$ty, Self> {
292 Ok(self)
293 }
294 fn kind(&self) -> TokenKind {
295 TokenKind::$ty
296 }
297 }
298 };
299}
300impl_token_tree_ext!(as_ident, into_ident, Ident);
301impl_token_tree_ext!(as_punct, into_punct, Punct);
302impl_token_tree_ext!(as_literal, into_literal, Literal);
303impl TokenTreeExt for Group {
304 fn as_group(&self) -> Option<&Group> {
305 Some(self)
306 }
307
308 fn into_group(self) -> Result<Group, Self> {
309 Ok(self)
310 }
311
312 fn kind(&self) -> TokenKind {
313 TokenKind::Group
314 }
315
316 fn is_solid_group(&self) -> bool {
317 self.delimiter() != Delimiter::None
318 }
319
320 fn is_delimiter(&self, delimiter: Delimiter) -> bool {
321 self.delimiter() == delimiter
322 }
323}
324
325pub trait Unsuffixed {
327 fn unsuffixed(self) -> Literal;
328}
329pub trait Suffixed {
331 fn suffixed(self) -> Literal;
332}
333macro_rules! impl_unsuffixes {
334 ( $($ty:ty: $unsuffixed:ident $($suffixed:ident)?);+ $(;)? ) => {
335 $(
336 #[doc = concat!(
337 "Call [`Literal::",
338 stringify!($unsuffixed),
339 "`]",
340 )]
341 impl Unsuffixed for $ty {
342 fn unsuffixed(self) -> Literal {
343 Literal::$unsuffixed(self)
344 }
345 }
346
347 $(
348 #[doc = concat!(
349 "Call [`Literal::",
350 stringify!($unsuffixed),
351 "`]",
352 )]
353 impl Suffixed for $ty {
354 fn suffixed(self) -> Literal {
355 Literal::$suffixed(self)
356 }
357 }
358 )?
359 )*
360 };
361}
362impl_unsuffixes! {
363 i8: i8_unsuffixed i8_suffixed;
364 i16: i16_unsuffixed i16_suffixed;
365 i32: i32_unsuffixed i32_suffixed;
366 i64: i64_unsuffixed i64_suffixed;
367 i128: i128_unsuffixed i128_suffixed;
368 u8: u8_unsuffixed u8_suffixed;
369 u16: u16_unsuffixed u16_suffixed;
370 u32: u32_unsuffixed u32_suffixed;
371 u64: u64_unsuffixed u64_suffixed;
372 u128: u128_unsuffixed u128_suffixed;
373 f32: f32_unsuffixed f32_suffixed;
374 f64: f64_unsuffixed f64_suffixed;
375 usize: usize_unsuffixed usize_suffixed;
376 isize: isize_unsuffixed isize_suffixed;
377 char: character;
378 &str: string;
379 &[u8]: byte_string;
380}
381
382pub trait PunctsExt: AsRef<[u8]> {
383 fn puncts(&self) -> TokenStream {
385 puncts(self)
386 }
387
388 fn puncts_spanned(&self, span: Span) -> TokenStream {
390 puncts_spanned(self, span)
391 }
392}
393impl<T: AsRef<[u8]> + ?Sized> PunctsExt for T { }
394
395pub trait PunctExt {
396 fn punct(self, spacing: Spacing) -> Punct;
397}
398impl PunctExt for char {
399 fn punct(self, spacing: Spacing) -> Punct {
401 Punct::new(self, spacing)
402 }
403}
404
405pub trait StrExt {
406 fn ident(&self, span: Span) -> Ident;
407}
408impl StrExt for str {
409 fn ident(&self, span: Span) -> Ident {
411 Ident::new(self, span)
412 }
413}