const_gen_derive/
lib.rs

1use proc_macro::TokenStream;
2use proc_macro2::Span;
3use quote::quote;
4use syn;
5
6const DOC_ATTR: &str = "inherit_doc";
7const INNER_DOC_ATTR: &str = "inherit_docs";
8
9/// Derives the CompileConst trait for structs and enums. Requires that all
10/// fields also implement the CompileConst trait.
11#[proc_macro_derive(CompileConst, attributes(inherit_doc, inherit_docs))]
12pub fn const_gen_derive(input: TokenStream) -> TokenStream 
13{
14    impl_macro(&syn::parse(input).unwrap())
15}
16
17fn impl_macro(ast: &syn::DeriveInput) -> TokenStream 
18{
19    let name = &ast.ident;
20    let generics = &ast.generics;
21    let val_impl: proc_macro2::TokenStream = match &ast.data
22    {
23        syn::Data::Struct(data) => struct_val_handler(name, &data.fields),
24        syn::Data::Enum(data) => 
25        {
26            let arms: Vec<proc_macro2::TokenStream> = data.variants
27                .iter()
28                .map(|v| enum_val_handler(name, &v.ident, &v.fields))
29                .collect();
30            quote!
31            {
32                format!("{}", match self
33                {
34                    #( #arms, )*
35                })
36            }
37        }
38        syn::Data::Union(data) => 
39        {
40            let vis = get_field_visibilities(&data.fields.named).into_iter().next().unwrap();
41            let ident = get_field_idents(&data.fields.named).into_iter().next().unwrap();
42            quote!
43            {
44                format!
45                (
46                    "{} {} {{ {}: {}}}", 
47                    stringify!(#vis), 
48                    stringify!(#name), 
49                    stringify!(#ident), 
50                    self.#ident.const_val()
51                )
52            }
53        }
54    };
55    let (doc_attr, inner_doc_attr) = get_docs(&ast.attrs);
56    let def_impl: proc_macro2::TokenStream = match &ast.data
57    {
58        syn::Data::Struct(data) => struct_def_handler(name, generics, &data.fields, inner_doc_attr),
59        syn::Data::Enum(data) => enum_def_handler(name, generics, data.variants.iter().collect(), inner_doc_attr),
60        syn::Data::Union(data) => 
61        {
62            let docs = get_field_docs(&data.fields.named, inner_doc_attr);
63            let vis = get_field_visibilities(&data.fields.named);
64            let idents = get_field_idents(&data.fields.named);
65            let types = get_field_types(&data.fields.named);
66            quote!
67            {
68                let mut f = String::new();
69                #( f.push_str(&format!("{} {} {}: {}, ", stringify!(#docs), stringify!(#vis), stringify!(#idents), <#types>::const_type())); )*
70                format!
71                (
72                    "union {}{}{{ {}}}", 
73                    stringify!(#name), 
74                    stringify!(#generics), 
75                    f
76                )
77            }
78        }
79    };
80    let doc_attr = doc_attr.map_or(String::new(), |attr| quote!(#attr).to_string());
81    let gen = quote!
82    {
83        impl const_gen::CompileConst for #name #generics
84        {
85            fn const_type() -> String
86            {
87                String::from(stringify!(#name))
88            }
89
90            fn const_val(&self) -> String
91            {
92                #val_impl
93            }
94
95            fn const_definition(attrs: &str, vis: &str) -> String
96            {
97                let mut definition = String::from(attrs);
98                definition += #doc_attr;
99                definition += " ";
100                definition += vis;
101                definition += " ";
102                definition += &{#def_impl};
103                definition
104            }
105        }
106    };
107    gen.into()
108}
109
110/// Generate a struct definition
111fn struct_def_handler(name: &syn::Ident, generics: &syn::Generics, fields: &syn::Fields, inner_doc_attr: bool) -> proc_macro2::TokenStream
112{
113    match fields
114    {
115        syn::Fields::Named(f) => 
116        {
117            let vis = get_field_visibilities(&f.named);
118            let idents = get_field_idents(&f.named);
119            let types = get_field_types(&f.named);
120            let docs = get_field_docs(&f.named, inner_doc_attr);
121            quote!
122            {
123                let mut f = String::new();
124                #( f.push_str(&format!("{} {} {}: {}, ", stringify!(#docs), stringify!(#vis), stringify!(#idents), <#types>::const_type())); )*
125                format!
126                (
127                    "struct {}{}{{ {}}}", 
128                    stringify!(#name), 
129                    stringify!(#generics), 
130                    f
131                )
132            }
133        },
134        syn::Fields::Unnamed(f) => 
135        {
136            let types = get_field_types(&f.unnamed);
137            quote!
138            {
139                let mut f = String::new();
140                #( f.push_str(&format!("{},", <#types>::const_type())); )*
141                format!
142                (
143                    "struct {}{}({});", 
144                    stringify!(#name), 
145                    stringify!(#generics), 
146                    f
147                )
148            }
149        },
150        syn::Fields::Unit => quote!(format!("struct {}{};", stringify!(#name), stringify!(#generics)))
151    } 
152}
153
154/// Generate a struct constructor
155fn struct_val_handler(name: &syn::Ident, fields: &syn::Fields) -> proc_macro2::TokenStream
156{
157    match fields
158    {
159        syn::Fields::Named(f) => 
160        {
161            let idents = get_field_idents(&f.named);
162            quote!
163            {
164                let mut f = String::new();
165                #( f.push_str(&format!("{}: {}, ", stringify!(#idents), self.#idents.const_val())); )*
166                format!
167                (
168                    "{} {{ {}}}", 
169                    stringify!(#name), 
170                    f
171                )
172            }
173        },
174        syn::Fields::Unnamed(f) => 
175        {
176            let mut counter = 0;
177            let vals: Vec<_> = f.unnamed.iter()
178                .map(|_|{let next = counter; counter += 1; next})
179                .map(syn::Index::from).collect();
180            quote!
181            {
182                let mut f = String::new();
183                #( f.push_str(&format!("{},", self.#vals.const_val())); )*
184                format!
185                (
186                    "{}({})", 
187                    stringify!(#name), 
188                    f
189                )
190            }
191        },
192        syn::Fields::Unit => quote!(stringify!(#name))
193    } 
194}
195
196/// Generate an enum constructor
197fn enum_val_handler(name: &syn::Ident, var_name: &syn::Ident, fields: &syn::Fields) -> proc_macro2::TokenStream
198{
199    let constructor = match fields
200    {
201        syn::Fields::Named(f) => 
202        {
203            let idents = get_field_idents(&f.named);
204            quote!
205            {{
206                let mut f = String::new();
207                #( f.push_str(&format!("{}:{},", stringify!(#idents), #idents.const_val())); )*
208                format!
209                (
210                    "{}::{}{{{}}}", 
211                    stringify!(#name), 
212                    stringify!(#var_name), 
213                    f
214                )
215            }}
216        },
217        syn::Fields::Unnamed(f) => 
218        {
219            let mut counter = 0;
220            let idents: Vec<syn::Ident> = f.unnamed.iter()
221                .map(|_|
222                {
223                    let new_ident = syn::Ident::new(&format!("idnt{}", counter), Span::call_site());
224                    counter += 1;
225                    new_ident
226                })
227                .collect();
228            quote!
229            {{
230                let mut f = String::new();
231                #( f.push_str(&format!("{},", #idents.const_val())); )*
232                format!
233                (
234                    "{}::{}({})", 
235                    stringify!(#name), 
236                    stringify!(#var_name), 
237                    f
238                )
239            }}
240        },
241        syn::Fields::Unit => quote!(format!("{}::{}", stringify!(#name), stringify!(#var_name)))
242    };
243    match fields
244    {
245        syn::Fields::Named(f) => 
246        {
247            let new_idents = get_field_idents(&f.named);
248            quote!(Self::#var_name {#(#new_idents, )*} => #constructor)
249        },
250        syn::Fields::Unnamed(f) => 
251        {
252            let mut counter = 0;
253            let new_idents: Vec<syn::Ident> = f.unnamed.iter()
254                .map(|_|
255                {
256                    let new_ident = syn::Ident::new(&format!("idnt{}", counter), Span::call_site());
257                    counter += 1;
258                    new_ident
259                })
260                .collect();
261            quote!(Self::#var_name (#(#new_idents, )*) => #constructor)
262        }
263        syn::Fields::Unit => quote!(Self::#var_name => #constructor)
264    }
265}
266
267/// Generate an enum definition
268fn enum_def_handler(name: &syn::Ident, generics: &syn::Generics, variants: Vec<&syn::Variant>, inner_doc_attr: bool) -> proc_macro2::TokenStream
269{
270    let arms: Vec<proc_macro2::TokenStream> = variants.into_iter()
271        .map(|v| enum_variant_def_handler(&v.attrs, &v.ident, &v.fields, inner_doc_attr))
272        .collect();
273    quote!
274    {
275        format!("enum {}{}{{ {} }}", stringify!(#name), stringify!(#generics), #(#arms+)&* "")
276    }
277}
278
279/// Generate an enum variant definition
280fn enum_variant_def_handler(attributes: &[syn::Attribute], var_name: &syn::Ident, fields: &syn::Fields, inner_doc_attr: bool) -> proc_macro2::TokenStream
281{
282    let doc_attr = get_inner_docs(attributes, inner_doc_attr);
283    match fields
284    {
285        syn::Fields::Named(f) => 
286        {
287            let idents = get_field_idents(&f.named);
288            let types = get_field_types(&f.named);
289            quote!
290            {{
291                let mut f = String::new();
292                #( f.push_str(&format!("{}:{},", stringify!(#idents), <#types>::const_type())); )*
293                format!
294                (
295                    "{} {}{{{}}},", 
296                    stringify!(#doc_attr),
297                    stringify!(#var_name), 
298                    f
299                )
300            }}
301        },
302        syn::Fields::Unnamed(f) => 
303        {
304            let types = get_field_types(&f.unnamed);
305            quote!
306            {{
307                let mut f = String::new();
308                #( f.push_str(&format!("{},", <#types>::const_type())); )*
309                format!
310                (
311                    "{} {}({}),", 
312                    stringify!(#doc_attr),
313                    stringify!(#var_name), 
314                    f
315                )
316            }}
317        },
318        syn::Fields::Unit => quote!(format!("{} {},", stringify!(#doc_attr), stringify!(#var_name)))
319    }
320}
321
322/// Get visibility of named fields
323fn get_field_visibilities(fields: &syn::punctuated::Punctuated<syn::Field, syn::token::Comma>) -> Vec<&syn::Visibility>
324{
325    fields.iter().map(|field|&field.vis).collect()
326}
327
328/// Get identifiers of named fields
329fn get_field_idents(fields: &syn::punctuated::Punctuated<syn::Field, syn::token::Comma>) -> Vec<&Option<syn::Ident>>
330{
331    fields.iter().map(|field|&field.ident).collect()
332}
333
334/// Get types of fields
335fn get_field_types(fields: &syn::punctuated::Punctuated<syn::Field, syn::token::Comma>) -> Vec<&syn::Type>
336{
337    fields.iter().map(|field|&field.ty).collect()
338}
339
340/// Get docs for fields
341fn get_field_docs(fields: &syn::punctuated::Punctuated<syn::Field, syn::token::Comma>, inner_doc_attr: bool) -> Vec<Option<syn::Attribute>>
342{
343    fields.iter().map(|field| get_inner_docs(&field.attrs, inner_doc_attr)).collect()
344}
345
346/// Parse DOC_ATTR to inherit docs
347fn get_docs(attrs: &[syn::Attribute]) -> (Option<syn::Attribute>, bool)
348{
349    let mut inner_doc_attr = false;
350    if attrs
351        .iter()
352        .any(|assoc_attr| 
353        {
354            if assoc_attr.path.is_ident(INNER_DOC_ATTR)
355            {
356                inner_doc_attr = true;
357            }
358            assoc_attr.path.is_ident(DOC_ATTR) || inner_doc_attr
359        })
360    {
361        (attrs.iter()
362            .filter(|assoc_attr| assoc_attr.path.is_ident("doc"))
363            .next()
364            .map(syn::Attribute::clone), inner_doc_attr)
365    }
366    else
367    {
368        (None, inner_doc_attr)
369    }
370}
371
372/// Parse INNER_DOC_ATTR to inherit docs for fields or variants
373fn get_inner_docs(attrs: &[syn::Attribute], inner_doc_attr: bool) -> Option<syn::Attribute>
374{
375    attrs.iter()
376        .filter(|assoc_attr| assoc_attr.path.is_ident("doc"))
377        .next()
378        .filter(|_| inner_doc_attr || attrs.iter().any(|attr| attr.path.is_ident("inherit_doc")))
379        .map(syn::Attribute::clone)
380}