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