enum_generator_macros/
lib.rs

1#![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 ast: syn::FieldsNamed = syn::parse(tokens).unwrap();
69    // let mut generator_function: TokenStream = "pub enum FunctionKey".parse().unwrap();
70    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    // println!("{}", quote! { #definition });
97    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        //println!("{} : {}", variant_name, end.ident.to_string() );
147        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}