rotary_permutator_derive/
lib.rs1
2
3extern crate proc_macro;
4extern crate syn;
5#[macro_use]
6extern crate quote;
7
8use proc_macro::TokenStream;
9
10#[proc_macro_derive(EnumRotor)]
11pub fn derive_rotor(input: TokenStream) -> TokenStream {
12 let input = syn::parse_macro_input!(input as syn::DeriveInput);
13 let enum_ident = &input.ident;
14
15 let first_elem = match &input.data {
16 syn::Data::Enum(syn::DataEnum {variants, ..}) => {
17 &variants[0].ident
18
19 }
20 _ => panic!("only enums supported")
21 };
22 let rotor_default = quote! {
23 impl std::default::Default for #enum_ident {
24 fn default() -> Self {
25 Self::#first_elem
26 }
27 }
28 };
29 let rotor_engine_struct = quote! {
30 #[derive(Debug, Clone)]
31 pub struct RotorEngine {
32 blocks: Vec<#enum_ident>,
33 trigger: Option<usize>,
34 final_trigger: bool,
35 }
36 };
37
38 let rotor_engine_init = gen_rotor_engine_init(&input);
39 let rotor_engine_rot_at = gen_rotor_engine_rot_at(&input);
40 let rotor_engine_impl = quote! {
41 impl RotorEngine {
42 #rotor_engine_init
43 #rotor_engine_rot_at
44 }
45 };
46
47 let iterator_for_rotor_engine = gen_iterator_for_rotator_engine(&input);
48
49 quote! {
50 #rotor_engine_struct
51 #rotor_default
52 #rotor_engine_impl
53 #iterator_for_rotor_engine
54
55 }.into()
56}
57
58fn gen_rotor_engine_init(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
59
60 let enum_ident = &ast.ident;
61 quote! {
62 pub fn init(size: usize) -> Self {
69 let mut blocks = Vec::new();
70 for _i in 0..size{
71 blocks.push(#enum_ident::default());
72 }
73 Self { blocks, trigger: None, final_trigger: false }
74 }
75
76 }
77}
78
79fn gen_rotor_engine_rot_at(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
80 let enum_ident = &ast.ident;
81
82 let mut match_elems_impl = quote!{};
83 let (first_elem, last_elem) = match &ast.data {
84 syn::Data::Enum(syn::DataEnum {variants, ..}) => {
85 let mut last_elem = &variants[0].ident;
86 for i in 0..variants.len() {
87 if i != variants.len() - 1 {
88 let current_elem = &variants[i+1].ident;
89 let stmt = quote! {
90 #enum_ident::#last_elem => self.blocks[index] = #enum_ident::#current_elem,
91 };
92 match_elems_impl.extend(stmt);
93 last_elem = current_elem;
94 }
95
96 }
97 (&variants[0].ident, &variants[variants.len() - 1].ident)
98
99 }
100 _ => panic!("only enums supported")
101 };
102
103 quote! {
104 fn rot_at(&mut self, index: usize) {
105 match self.blocks[index] {
106 #match_elems_impl
107 #enum_ident::#last_elem => {
108
109 if index > 0 {
110 self.trigger = Some(index - 1);
111 self.blocks[index] = #enum_ident::#first_elem;
112 } else {
113 self.final_trigger = true;
114 }
115 }
116 }
117 }
118 }
119}
120
121fn gen_iterator_for_rotator_engine(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
122 let enum_ident = &ast.ident;
123 quote! {
124 impl std::iter::Iterator for RotorEngine {
125 type Item = Vec<#enum_ident>;
126 fn next(&mut self) -> Option<Self::Item> {
127
128 while let Some(idx) = self.trigger {
129 self.trigger = None;
130 self.rot_at(idx);
131 }
132 let capture = self.blocks.clone();
133 self.rot_at(self.blocks.len() - 1);
134
135 if self.final_trigger {
136 return None;
137 }
138
139 Some(capture)
140 }
141 }
142 }
143
144}