enum_generator_macros/
lib.rs1#![allow(dead_code)]
2#![allow(unused_imports, unused_mut, unused_variables)]
3use syn::token;
4use syn::token::Comma;
5use syn::parse::{Parse, ParseStream};
6use syn::{ Token, ItemEnum,
7 Attribute, Ident,
8 Generics, Lit, Visibility,
9 parse_macro_input, Data,
10 DeriveInput, Variant };
11use syn::punctuated::Punctuated;
12use proc_macro::TokenStream;
13use quote::quote;
14use quote::ToTokens;
15use quote::{format_ident};
16use quote::TokenStreamExt;
17use proc_macro::TokenTree;
18
19
20#[derive(syn_derive::Parse, syn_derive::ToTokens)]
21struct GeneratedEnum {
22 #[parse(Attribute::parse_outer)]
23 #[to_tokens(|tokens, val| tokens.append_all(val))]
24 attrs: Vec<Attribute>,
25
26 vis: Visibility,
27
28 enum_token: Token![enum],
29
30 ident: Ident,
31
32 generics: Generics,
33
34 #[syn(braced)]
35 brace_token: token::Brace,
36
37 #[syn(in = brace_token)]
38 #[parse(Variant::parse, boxed)]
39 leading: Box<Variant>,
40
41 #[syn(in = brace_token)]
42 template_sep: Token![:],
43
44 #[syn(in = brace_token)]
45 template_prefix: Option<Ident>,
46
47 #[syn(in = brace_token)]
48 id_place: Token![_],
49
50 #[syn(in = brace_token)]
51 template_suffix: Option<Ident>,
52
53 #[syn(in = brace_token)]
54 comma: Token![,],
55
56 #[syn(in = brace_token)]
57 dots: Token![...],
58
59 #[syn(in = brace_token)]
60 #[parse(Variant::parse, boxed)]
61 end: Box<Variant>
62}
63
64
65#[proc_macro]
66pub fn generate_enum(tokens: TokenStream) -> TokenStream {
67 let generated_enum: GeneratedEnum = syn::parse(tokens).expect("Could not parse as GeneratedEnum");
68 let leading_variant = *generated_enum.leading.clone();
71 let end_variant = *generated_enum.end.clone();
72 if let Some(prefix) = generated_enum.template_prefix.clone() {
73 assert!(leading_variant.ident.to_string().starts_with(&prefix.to_string()));
74 assert!(end_variant.ident.to_string().starts_with(&prefix.to_string()));
75 }
76
77 if let Some(suffix) = generated_enum.template_suffix.clone() {
78 assert!(leading_variant.ident.to_string().ends_with(&suffix.to_string()));
79 assert!(end_variant.ident.to_string().ends_with(&suffix.to_string()));
80 }
81
82 let definition = ItemEnum {
83 attrs: generated_enum.attrs,
84 vis: generated_enum.vis,
85 enum_token: generated_enum.enum_token,
86 ident: generated_enum.ident,
87 generics: generated_enum.generics,
88 brace_token: generated_enum.brace_token,
89 variants: make_enum_variants(
90 (generated_enum.template_prefix,
91 generated_enum.template_suffix),
92 leading_variant,
93 end_variant)
94 };
95
96 quote! { #definition }.into()
98}
99
100
101fn make_enum_variants(template: (Option<Ident>, Option<Ident>), leading: Variant, end: Variant) -> Punctuated<Variant, Comma> {
102 let mut variants = Punctuated::<Variant, Token![,]>::new();
103 variants.push(leading.clone());
104
105 let leading_variant_name = leading.ident.to_string();
106 let leading_variant_name = leading_variant_name.as_bytes();
107
108 let prefix = if let Some(prefix) = template.0 {
109 prefix.to_string()
110 } else {
111 "".to_string()
112 };
113
114 let suffix = if let Some(suffix) = template.1 {
115 suffix.to_string()
116 } else {
117 "".to_string()
118 };
119
120
121 let starting_char = leading_variant_name[prefix.len()..prefix.len()+1][0];
122 let start = unsafe { String::from_utf8_unchecked(
123 leading_variant_name[prefix.len()..leading_variant_name.len() - suffix.len()].to_vec())
124 };
125 let name_generator: Box<dyn Fn(usize) -> String> = if let Ok(starting_number) = start.parse::<usize>() {
126 let suffix = suffix.clone();
127 let number_sequence_gen = move |i: usize| -> String { format!("{}{}{}", &prefix, &(i+starting_number).to_string(), &suffix) };
128 Box::new(number_sequence_gen)
129 } else {
130 if start.len() != 1 {
131 panic!("could not identify templating starting index/ starting char.");
132 }
133
134 let suffix = suffix.clone();
135 let letter_sequence_gen = move |i: usize| format!("{}{}{}",
136 &prefix,
137 &((starting_char + i as u8) as char).to_string(),
138 &suffix);
139 Box::new(letter_sequence_gen)
140 };
141
142 let mut i = 1;
143 loop {
144 let mut variant = leading.clone();
145 let variant_name = &name_generator(i);
146 let ident = Ident::new(&variant_name, proc_macro2::Span::call_site());
148 variant.ident = ident;
149 variants.push(variant);
150 if variants.len() > 500 {
151 panic!("GenerateEnum Attempted to generate enum with more than 500 variants");
152 } else if *variant_name == end.ident.to_string() {
153 break;
154 }
155 i += 1;
156 }
157
158 variants
159}