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