1use proc_macro::{
24 TokenStream,
25 Delimiter,
26 TokenTree,
27 Literal,
28 Spacing,
29 Group,
30 Ident,
31 Punct,
32 Span
33};
34
35use core::cmp::{PartialOrd, PartialEq, Ordering, Ord, Eq};
36use core::num::NonZeroUsize;
37use std::vec::Vec;
38
39
40
41
42
43#[allow(unused)]
44trait SubposData<S:Subpos> {
45 fn position(&self) -> &S;
46
47 fn data(&self) -> &S::Data;
48}
49
50
51
52trait Subpos:PartialEq {
53 type Data:PartialEq;
54
55 const ASSOCIATED_STAGE:LexicalPosStage;
56}
57
58impl Subpos for () {
59 type Data = ();
60
61 const ASSOCIATED_STAGE:LexicalPosStage = LexicalPosStage::Start;
62}
63
64
65
66
67
68#[derive(PartialEq, Clone, Copy, Eq)]
69#[repr(u8)]
70enum GenericParamType {
71 Type = 0,
72 Const = 1,
73 Lifetime = 2
74}
75
76impl GenericParamType {
77 #[inline(always)] pub fn prefix(&self) -> Option<TokenTree> {
78 match self {
79 Self::Type => { None }
80
81 Self::Const => { Some( TokenTree::Ident(Ident::new("const", Span::call_site())) ) }
82
83 Self::Lifetime => { Some( TokenTree::Punct(Punct::new('\'', Spacing::Joint)) ) }
84 }
85 }
86}
87
88use GenericParamType::*;
89
90
91
92#[derive(PartialOrd, PartialEq, Clone, Copy, Ord, Eq)]
94enum GenericParamPos {
95 Name = 0,
96 Bounds = 1,
97 DefaultAssignment = 2
98}
99
100impl Subpos for GenericParamPos {
101 type Data = NonZeroUsize;
102
103 const ASSOCIATED_STAGE:LexicalPosStage = LexicalPosStage::Generics;
104}
105
106
107
108#[derive(PartialOrd, PartialEq,Clone, Debug, Copy, Ord, Eq)]
109#[repr(usize)]
110enum LexicalPosStage {
111 Start = 0,
113 Name = 1,
115 AfterName = 2,
117 Generics = 3,
121 AfterGenerics = 4,
123 WhereClause = 5,
125 EnumBody = 6
127}
128
129
130
131#[derive(Clone, Copy)]
132union LexicalPosData {
133 enum_body:(EnumBodyPos, <EnumBodyPos as Subpos>::Data),
134 generics:(GenericParamPos, <GenericParamPos as Subpos>::Data),
135 usize:usize,
136 _marker:()
137}
138
139impl SubposData<EnumBodyPos> for LexicalPosData {
140 fn position(&self) -> &EnumBodyPos { unsafe { &self.enum_body.0 } }
141
142 fn data(&self) -> &<EnumBodyPos as Subpos>::Data { unsafe { &self.enum_body.1 } }
143}
144
145impl SubposData<GenericParamPos> for LexicalPosData {
146 fn position(&self) -> &GenericParamPos { unsafe { &self.generics.0 } }
147
148 fn data(&self) -> &<GenericParamPos as Subpos>::Data { unsafe { &self.generics.1 } }
149}
150
151
152
153#[derive(Clone)]
154struct GenericParam {
155 pub default_item:Option<TokenStream>,
156 pub param_type:GenericParamType,
157 pub bounds:Option<TokenStream>,
158 pub name:Option<Ident>
159}
160
161impl GenericParam {
162 pub const DEFAULT:Self = Self {default_item: None, param_type: GenericParamType::Type, bounds: None, name: None};
163}
164
165
166
167#[derive(PartialEq, Clone, Copy, Eq)]
168enum EnumBodyPos {
169 Variant,
171 AfterVariant,
174 Definition
177}
178
179impl Subpos for EnumBodyPos {
180 type Data = ();
181
182 const ASSOCIATED_STAGE:LexicalPosStage = LexicalPosStage::EnumBody;
183}
184
185
186
187#[derive(Clone, Copy)]
189struct LexicalPos {
190 pub stage:LexicalPosStage,
191 pub data:LexicalPosData
192}
193
194impl LexicalPos {
195 #[inline(always)] pub const fn after_generics() -> Self {
196 Self {
197 stage: LexicalPosStage::AfterGenerics,
198 data: LexicalPosData { _marker: () }
199 }
200 }
201
202 #[inline(always)] pub const fn where_clause() -> Self {
203 Self {
204 stage: LexicalPosStage::WhereClause,
205 data: LexicalPosData { usize: 0 }
206 }
207 }
208
209 #[inline(always)] pub const fn after_name() -> Self {
210 Self {
211 stage: LexicalPosStage::AfterName,
212 data: LexicalPosData { _marker: () }
213 }
214 }
215
216 #[inline(always)] pub const fn enum_body(pos:EnumBodyPos) -> Self {
217 Self {
218 stage: LexicalPosStage::EnumBody,
219 data: LexicalPosData { enum_body: (pos, ()) }
220 }
221 }
222
223 #[inline(always)] pub const fn generics(depth:<GenericParamPos as Subpos>::Data, pos:GenericParamPos) -> Self {
224 Self {
225 stage: LexicalPosStage::Generics,
226 data: LexicalPosData { generics: (pos, depth) }
227 }
228 }
229
230 #[inline(always)] pub const fn start() -> Self {
231 Self {
232 stage: LexicalPosStage::Start,
233 data: LexicalPosData { _marker: () }
234 }
235 }
236
237 #[inline(always)] pub const fn name() -> Self {
238 Self {
239 stage: LexicalPosStage::Name,
240 data: LexicalPosData { _marker: () }
241 }
242 }
243
244 #[inline(always)] pub const fn enum_body_can_follow(&self) -> bool {
246 self.stage as usize == LexicalPosStage::AfterName as usize
247 || self.stage as usize == LexicalPosStage::AfterGenerics as usize
248 || (
249 self.stage as usize == LexicalPosStage::WhereClause as usize
250 && unsafe { self.data.usize } == 0
251 )
252 }
253
254 pub fn after_substate<S>(&self, substate:S) -> bool where LexicalPosData:SubposData<S>, S:Subpos+Ord {
255 self.stage == S::ASSOCIATED_STAGE
256 && <LexicalPosData as SubposData<S>>::position(&self.data).gt(&substate)
257 }
258
259 #[inline(always)] pub const fn generics_depth(&self) -> usize {
263 if self.stage as usize != LexicalPosStage::Generics as usize { return 0; }
264 unsafe {
265 self.data.generics.1.get()
266 }
267 }
268
269 pub fn in_substate<S>(&self, substate:S) -> bool where LexicalPosData:SubposData<S>, S:Subpos {
270 self.stage == S::ASSOCIATED_STAGE
271 && <LexicalPosData as SubposData<S>>::position(&self.data).eq(&substate)
272 }
273}
274
275impl PartialOrd<LexicalPosStage> for LexicalPos {
276 #[inline(always)] fn partial_cmp(&self, rhs:&LexicalPosStage) -> Option<Ordering> { Some(self.stage.cmp(rhs)) }
277}
278
279impl PartialOrd for LexicalPos {
280 #[inline(always)] fn partial_cmp(&self, rhs:&Self) -> Option<Ordering> { Some(self.cmp(rhs)) }
282}
283
284impl PartialEq<LexicalPosStage> for LexicalPos {
285 #[inline(always)] fn eq(&self, rhs:&LexicalPosStage) -> bool { self.stage.eq(rhs) }
286}
287
288impl PartialEq for LexicalPos {
289 #[inline] fn eq(&self, rhs:&Self) -> bool {
290 match (self.stage, rhs.stage) {
291 (LexicalPosStage::Generics, LexicalPosStage::Generics) => { unsafe {
292 self.data.generics == rhs.data.generics
293 } }
294
295 (LexicalPosStage::EnumBody, LexicalPosStage::EnumBody) => { unsafe {
296 self.data.enum_body == rhs.data.enum_body
297 } }
298
299 (x, y) => { x == y }
300 }
301 }
302}
303
304impl Ord for LexicalPos {
305 #[inline(always)] fn cmp(&self, rhs:&Self) -> Ordering { self.stage.cmp(&rhs.stage) }
307}
308
309impl Eq for LexicalPos {}
310
311
312
313
314
315#[inline] fn cmp_punct(punct_a:Option<Punct>, punct_b:Option<Punct>) -> bool {
321 let (Some(a), Some(b)) = (punct_a, punct_b) else { return false; };
322
323 a.as_char() == b.as_char()
324 && a.spacing() == b.spacing()
325}
326
327
328
329#[proc_macro_attribute]
337pub fn enum_info(_attr:TokenStream, item:TokenStream) -> TokenStream {
338 let mut current_generic_param = GenericParam::DEFAULT;
339 let mut prev_tok_as_punct = None;
340 let mut generic_params = Vec::<GenericParam>::new();
341 let mut variant_names = Vec::<Ident>::new();
342 let mut where_clause = None;
343 let mut variant_ct = 0;
344 let mut all_unit = true;
345 let mut position = LexicalPos::start();
346 let mut output = item.clone();
347 let mut name = String::new();
348
349 for token in item {
350 let mut generic_param_update = false;
351 let mut tok_as_punct = None;
352 let mut appendage = None;
353
354 match token {
356 TokenTree::Group(g) if position.enum_body_can_follow() && !cmp_punct(prev_tok_as_punct.clone(), Some(Punct::new('!', Spacing::Alone))) && g.delimiter() == Delimiter::Brace => {
357 position = LexicalPos::enum_body(EnumBodyPos::Variant);
358 for subtoken in g.stream() {
359 match subtoken {
360 TokenTree::Group(_) if position.in_substate(EnumBodyPos::AfterVariant) => {
361 if !all_unit { continue; }
362
363 variant_names.clear();
364 variant_names.shrink_to(0);
365 all_unit = false;
366 }
367
368 TokenTree::Ident(i) if position.in_substate(EnumBodyPos::Variant) => {
369 position.data.enum_body.0 = EnumBodyPos::AfterVariant;
370 variant_ct += 1;
371
372 if all_unit { variant_names.push(i); }
373 }
374
375 TokenTree::Punct(p) => {
376 match p.as_char() {
377 '=' => {
378 position.data.enum_body.0 = EnumBodyPos::Definition;
379 }
380
381 ',' => {
382 position.data.enum_body.0 = EnumBodyPos::Variant;
383 }
384
385 _ => {}
386 }
387 }
388
389 _ => {}
390 }
391 }
392 }
393
394 TokenTree::Group(g) if (position.after_substate(GenericParamPos::Name) || position == LexicalPosStage::WhereClause) => {
395 appendage = Some(TokenTree::Group(g));
396 }
397
398 TokenTree::Ident(i) if position == LexicalPosStage::Name => {
399 name = i.to_string();
400 position = LexicalPos::after_name();
401 continue;
402 }
403
404 TokenTree::Ident(i) => {
405 match i.to_string().as_str() {
406 "enum" if position == LexicalPosStage::Start => {
407 position = LexicalPos::name();
408 },
409
410 "const" if position.generics_depth() == 1
412 && current_generic_param.name.is_none()
413 && current_generic_param.param_type == Type => { current_generic_param.param_type = Const; },
414
415 "where" if position == LexicalPosStage::AfterName || position == LexicalPosStage::AfterGenerics => {
417 position = LexicalPos::where_clause();
418 }
419
420 _ if position.in_substate(GenericParamPos::Name) && current_generic_param.name.is_none() => {
421 current_generic_param.name = Some(i);
422 },
423
424 _ if (position.after_substate(GenericParamPos::Name) || position == LexicalPosStage::WhereClause) => {
425 appendage = Some(TokenTree::Ident(i));
426 }
427
428 _ => {}
429 }
430 }
431
432 TokenTree::Punct(p) => {
433 let c = p.as_char();
434 tok_as_punct = Some(p.clone());
435
436 match c {
437 '\'' if position.in_substate(GenericParamPos::Name)
439 && current_generic_param.name.is_none()
440 && current_generic_param.param_type == Type => { current_generic_param.param_type = Lifetime; }
441
442 '<' => {
444 if position == LexicalPosStage::AfterName {
445 position = LexicalPos::generics(unsafe { NonZeroUsize::new_unchecked(1) }, GenericParamPos::Name);
446 continue;
447 } else if position.after_substate(GenericParamPos::Name) {
448 appendage = Some(TokenTree::Punct(p));
449 position.data.generics.1 = unsafe { position.data.generics }.1.saturating_add(1);
450 } else if position == LexicalPosStage::WhereClause {
451 unsafe { position.data.usize += 1; }
452 appendage = Some(TokenTree::Punct(p));
453 }
454 }
455
456 '>' if position == LexicalPosStage::Generics && !cmp_punct(prev_tok_as_punct.clone(), Some(Punct::new('-', Spacing::Joint))) => 'close_generics: {
458 if unsafe { position.data.generics }.1.get() == 1 {
459 generic_param_update = true;
460 position = LexicalPos::after_generics();
461
462 break 'close_generics;
463 }
464
465 position.data.generics.1 =
466 unsafe { NonZeroUsize::new_unchecked(position.data.generics.1.get().saturating_sub(1)) };
467 appendage = Some(TokenTree::Punct(p));
468 }
469
470 '>' if position == LexicalPosStage::WhereClause && !cmp_punct(prev_tok_as_punct.clone(), Some(Punct::new('-', Spacing::Joint))) => 'close_generics: { unsafe {
472 appendage = Some(TokenTree::Punct(p));
473
474 if position.data.usize < 1 { break 'close_generics; }
475 position.data.usize -= 1;
476 } }
477
478 ':' if position.in_substate(GenericParamPos::Name) => {
480 position.data.generics.0 = GenericParamPos::Bounds;
481 }
482
483 '=' if !position.after_substate(GenericParamPos::Bounds) && position == LexicalPosStage::Generics => {
485 position.data.generics.0 = GenericParamPos::DefaultAssignment;
486 }
487
488 ',' if position.generics_depth() == 1 => {
490 position.data.generics.0 = GenericParamPos::Name;
491 generic_param_update = true;
492 }
493
494 _ if (position.after_substate(GenericParamPos::Name) || position == LexicalPosStage::WhereClause) => {
495 appendage = Some(TokenTree::Punct(p));
496 }
497
498 _ => {}
499 }
500 }
501
502 TokenTree::Literal(l) if (position.after_substate(GenericParamPos::Name) || position == LexicalPosStage::WhereClause) => {
503 appendage = Some(TokenTree::Literal(l));
504 }
505
506 _ => {}
507 }
508
509 'append_generics: {
510 if let Some(appendage) = appendage {
511 let append_to:&mut Option<TokenStream>;
512 if position.in_substate(GenericParamPos::Bounds) {
513 append_to = &mut current_generic_param.bounds;
514 } else if position.in_substate(GenericParamPos::DefaultAssignment) {
515 append_to = &mut current_generic_param.default_item;
516 } else if position == LexicalPosStage::WhereClause {
517 append_to = &mut where_clause;
518 } else {
519 break 'append_generics;
520 }
521
522 if let &mut Some(ref mut append_to) = append_to {
523 append_to.extend([appendage].into_iter());
524 } else {
525 *append_to = Some(TokenStream::from_iter([appendage].into_iter()));
526 }
527 }
528 }
529
530 'add_generic_param: {
531 if current_generic_param.name.is_none() { break 'add_generic_param; }
532
533 if generic_param_update {
534 generic_params.push(current_generic_param);
535 current_generic_param = GenericParam::DEFAULT;
536 }
537 }
538
539 prev_tok_as_punct = tok_as_punct;
540 }
541
542 let mut r#impl = TokenStream::from_iter([
543 TokenTree::Punct(Punct::new('#', Spacing::Alone)),
545 TokenTree::Group(Group::new(
546 Delimiter::Bracket,
547 TokenStream::from_iter([
548 TokenTree::Ident(Ident::new("inline", Span::call_site())),
549 TokenTree::Group(Group::new(
550 Delimiter::Parenthesis,
551 TokenStream::from_iter([
552 TokenTree::Ident(Ident::new("always", Span::call_site()))
553 ].into_iter())
554 ))
555 ].into_iter())
556 )),
557
558 TokenTree::Punct(Punct::new('#', Spacing::Alone)),
562 TokenTree::Group(Group::new(
563 Delimiter::Bracket,
564 TokenStream::from_iter([
565 TokenTree::Ident(Ident::new("doc", Span::call_site())),
566 TokenTree::Punct(Punct::new('=', Spacing::Alone)),
567 TokenTree::Literal(Literal::string("Returns the number of variants this enum has."))
568 ].into_iter())
569 )),
570 TokenTree::Ident(Ident::new("pub", Span::call_site())),
571 TokenTree::Ident(Ident::new("const", Span::call_site())),
572 TokenTree::Ident(Ident::new("fn", Span::call_site())),
573 TokenTree::Ident(Ident::new("variant_count", Span::call_site())),
574 TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())),
575 TokenTree::Punct(Punct::new('-', Spacing::Joint)),
576 TokenTree::Punct(Punct::new('>', Spacing::Alone)),
577 TokenTree::Punct(Punct::new(':', Spacing::Joint)),
578 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
579 TokenTree::Ident(Ident::new("core", Span::call_site())),
580 TokenTree::Punct(Punct::new(':', Spacing::Joint)),
581 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
582 TokenTree::Ident(Ident::new("primitive", Span::call_site())),
583 TokenTree::Punct(Punct::new(':', Spacing::Joint)),
584 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
585 TokenTree::Ident(Ident::new("usize", Span::call_site())),
586
587 TokenTree::Group(Group::new(
589 Delimiter::Brace,
590 TokenStream::from_iter([
591 TokenTree::Literal(Literal::usize_unsuffixed(variant_ct))
592 ].into_iter())
593 ))
594 ].into_iter());
595
596 if all_unit {
598 let mut items = TokenStream::new();
599
600 for variant in variant_names {
602 items.extend([
603 TokenTree::Ident(Ident::new("Self", Span::call_site())),
604 TokenTree::Punct(Punct::new(':', Spacing::Joint)),
605 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
606 TokenTree::Ident(variant),
607 TokenTree::Punct(Punct::new(',', Spacing::Alone))
608 ])
609 }
610
611 r#impl.extend([
612 TokenTree::Ident(Ident::new("pub", Span::call_site())),
613 TokenTree::Ident(Ident::new("const", Span::call_site())),
614 TokenTree::Ident(Ident::new("VARIANTS", Span::call_site())),
615 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
616 TokenTree::Group(Group::new(
617 Delimiter::Bracket,
618 TokenStream::from_iter([
619 TokenTree::Ident(Ident::new("Self", Span::call_site())),
620 TokenTree::Punct(Punct::new(';', Spacing::Alone)),
621 TokenTree::Literal(Literal::usize_unsuffixed(variant_ct))
622 ].into_iter())
623 )),
624 TokenTree::Punct(Punct::new('=', Spacing::Alone)),
625 TokenTree::Group(Group::new(Delimiter::Bracket, items)),
626 TokenTree::Punct(Punct::new(';', Spacing::Alone))
627 ]);
628 }
629
630 let mut generics_names = TokenStream::new();
631 let mut generics_full = TokenStream::new();
632
633 if generic_params.len() > 0 {
634 generics_names.extend([TokenTree::Punct(Punct::new('<', Spacing::Alone))]);
635 generics_full.extend([TokenTree::Punct(Punct::new('<', Spacing::Alone))]);
636
637 for generic_item in generic_params {
638 if generic_item.param_type == Lifetime {
639 generics_names.extend([ unsafe { generic_item.param_type.prefix().unwrap_unchecked() } ]);
640 }
641
642 generics_names.extend([
643 TokenTree::Ident(unsafe { generic_item.name.clone().unwrap_unchecked() }),
644 TokenTree::Punct(Punct::new(',', Spacing::Alone))
645 ]);
646
647 if let Some(prefix) = generic_item.param_type.prefix() {
648 generics_full.extend([prefix]);
649 }
650
651 generics_full.extend([TokenTree::Ident(unsafe { generic_item.name.unwrap_unchecked() })]);
652
653 if let Some(suffix) = generic_item.bounds {
654 generics_full.extend([
655 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
656 TokenTree::Group(Group::new(Delimiter::None, suffix))
657 ]);
658 }
659
660 generics_full.extend([TokenTree::Punct(Punct::new(',', Spacing::Alone))]);
661 }
662
663 generics_names.extend([TokenTree::Punct(Punct::new('>', Spacing::Alone))]);
664 generics_full.extend([TokenTree::Punct(Punct::new('>', Spacing::Alone))]);
665 }
666
667 output.extend([
668 TokenTree::Ident(Ident::new("impl", Span::call_site())),
670 TokenTree::Group(Group::new(Delimiter::None, generics_full)),
671 TokenTree::Ident(Ident::new(&name, Span::call_site())),
672 TokenTree::Group(Group::new(Delimiter::None, generics_names)),
673
674 TokenTree::Group(
676 Group::new(
677 Delimiter::None,
678 where_clause.map(
679 |ts| {
680 let mut w = TokenStream::from_iter([TokenTree::Ident(Ident::new("where", Span::call_site()))]);
681 w.extend(ts);
682 w
683 }
684 ).unwrap_or_default()
685 )
686 ),
687
688 TokenTree::Group(Group::new(Delimiter::Brace, r#impl))
690 ]);
691 output
692}