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