rotary_permutator_derive/
lib.rs

1
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        /// Creates an initial state for the machine of a given size.
63        /// The size is the length of the permutation.
64        /// # Example
65        /// ```
66        /// let rotor_engine = RotorEngine::init(4);
67        /// ```
68        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}