quantifiable_derive/
lib.rs

1
2extern crate proc_macro;
3//#[macro_use]
4extern crate quote;
5extern crate syn;
6extern crate synstructure;
7
8use syn::{parse_macro_input, DeriveInput};
9use syn::spanned::Spanned;
10use quote::{quote, quote_spanned};
11
12
13// Look https://github.com/dtolnay/syn/blob/master/examples/heapsize/heapsize_derive/src/lib.rs for a more recent similar derive.
14
15//WITH proc_macro
16// #[proc_macro_derive(Quantifiable)]
17// pub fn quantifiable_macro_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
18// 	//let ast = syn::parse(input).unwrap();
19// 	let mut ast = syn::parse_macro_input(&input.to_string()).unwrap();
20// 	//let mut ast = syn::parse_macro_input!(input);
21// 	let style = synstructure::BindStyle::Ref.into();
22// 	//Collect all the fields into a total_memory method.
23// 	let total_memory_body = synstructure::each_field(&mut ast, &style, |binding| {
24// 			Some(quote! {
25// 					sum += ::quantify::Quantifiable::total_memory(#binding);
26// 					})
27// 			});
28// 	//Collect all the fields into a forecast_total_memory method.
29// 	let forecast_total_memory_body = synstructure::each_field(&mut ast, &style, |binding| {
30// 			Some(quote! {
31// 					sum += ::quantify::Quantifiable::forecast_total_memory(#binding);
32// 					})
33// 			});
34// 	//The name of the type for which we are implementing Quantifiable.
35// 	let name = &ast.ident;
36// 	let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
37// 	let mut where_clause = where_clause.clone();
38// 	//We added the where condition member_name:Quantifiable for each member.
39// 	for param in &ast.generics.ty_params {
40// 		where_clause.predicates.push(syn::WherePredicate::BoundPredicate(syn::WhereBoundPredicate {
41// 			bound_lifetimes: Vec::new(),
42// 			bounded_ty: syn::Ty::Path(None, param.ident.clone().into()),
43// 			//bounds: vec![syn::TypeParamBound::Trait(
44// 			bounds: vec![syn::TyParamBound::Trait(
45// 				syn::PolyTraitRef {
46// 					bound_lifetimes: Vec::new(),
47// 					trait_ref: syn::parse_path("::quantifiable::Quantifiable").unwrap(),
48// 				},
49// 				syn::TraitBoundModifier::None
50// 			)],
51// 		}))
52// 		//where_clause.predicates.push(syn::WherePredicate::Type(syn::PredicateType {
53// 		//	lifetimes: None,
54// 		//	bounded_ty: syn::Ty::Path(None, param.ident.clone().into()),
55// 		//	bounds: syn::parse_path("::quantifiable::Quantifiable").unwrap(),
56// 		//}))
57// 	}
58// 	//Build the token sequence.
59// 	let tokens = quote! {
60// 		impl #impl_generics ::quantify::Quantifiable for #name #ty_generics #where_clause {
61// #[inline]
62// #[allow(unused_variables, unused_mut, unreachable_code)]
63// 			fn total_memory(&self) -> usize {
64// 				let mut sum = 0;
65// 				match *self {
66// 					#total_memory_body
67// 				}
68// 				sum
69// 			}
70// 			fn print_memory_breakdown(&self)
71// 			{
72// 				unimplemented!();
73// 			}
74// 			fn forecast_total_memory(&self) -> usize
75// 			{
76// 				let mut sum = 0;
77// 				match *self {
78// 					#forecast_total_memory_body
79// 				}
80// 				sum
81// 			}
82// 		}
83// 	};
84// 	//tokens
85// 	tokens.to_string().parse().unwrap()
86// }
87
88//WITH proc_macro2 and syn-1.0
89// https://doc.rust-lang.org/proc_macro/index.html
90// https://docs.rs/proc-macro2/1.0.24/proc_macro2/index.html
91// https://docs.rs/syn/1.0.44/syn/index.html
92#[proc_macro_derive(Quantifiable)]
93pub fn quantifiable_macro_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
94	//let ast = syn::parse(input).unwrap();
95	//let mut ast = syn::parse_macro_input(&input.to_string()).unwrap();
96	//let input = proc_macro2::TokenStream::from(input);
97	let ast = parse_macro_input!(input as DeriveInput);
98	//let mut ast = syn::parse_macro_input!(input);
99	//let style = synstructure::BindStyle::Ref.into();
100	//Collect all the fields into a total_memory method.
101	//let total_memory_body = synstructure::each_field(&mut ast, &style, |binding| {
102	//		Some(quote! {
103	//				sum += ::quantify::Quantifiable::total_memory(#binding);
104	//				})
105	//		});
106	////Collect all the fields into a forecast_total_memory method.
107	//let forecast_total_memory_body = synstructure::each_field(&mut ast, &style, |binding| {
108	//		Some(quote! {
109	//				sum += ::quantify::Quantifiable::forecast_total_memory(#binding);
110	//				})
111	//		});
112	let total_memory_body = quantifiable_total_memory_expression(&ast.data);
113	let forecast_total_memory_body = quantifiable_forecast_total_memory_expression(&ast.data);
114	//The name of the type for which we are implementing Quantifiable.
115	let name = &ast.ident;
116	let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
117	let where_clause = where_clause.clone();
118	//We added the where condition member_name:Quantifiable for each member.
119	// for param in &ast.generics.ty_params {
120	// 	where_clause.predicates.push(syn::WherePredicate::BoundPredicate(syn::WhereBoundPredicate {
121	// 		bound_lifetimes: Vec::new(),
122	// 		bounded_ty: syn::Ty::Path(None, param.ident.clone().into()),
123	// 		//bounds: vec![syn::TypeParamBound::Trait(
124	// 		bounds: vec![syn::TyParamBound::Trait(
125	// 			syn::PolyTraitRef {
126	// 				bound_lifetimes: Vec::new(),
127	// 				trait_ref: syn::parse_path("::quantifiable::Quantifiable").unwrap(),
128	// 			},
129	// 			syn::TraitBoundModifier::None
130	// 		)],
131	// 	}))
132	// 	//where_clause.predicates.push(syn::WherePredicate::Type(syn::PredicateType {
133	// 	//	lifetimes: None,
134	// 	//	bounded_ty: syn::Ty::Path(None, param.ident.clone().into()),
135	// 	//	bounds: syn::parse_path("::quantifiable::Quantifiable").unwrap(),
136	// 	//}))
137	// }
138	//Build the token sequence.
139	let tokens = quote! {
140		impl #impl_generics crate::quantify::Quantifiable for #name #ty_generics #where_clause {
141#[inline]
142#[allow(unused_variables, unused_mut, unreachable_code)]
143			fn total_memory(&self) -> usize {
144				#total_memory_body
145			}
146			fn print_memory_breakdown(&self)
147			{
148				unimplemented!();
149			}
150			fn forecast_total_memory(&self) -> usize
151			{
152				#forecast_total_memory_body
153			}
154		}
155	};
156	//tokens
157	//tokens.to_string().parse().unwrap()
158	proc_macro::TokenStream::from(tokens)
159}
160
161///Create an expression for the total_memory method.
162fn quantifiable_total_memory_expression(data: &syn::Data) -> proc_macro2::TokenStream
163{
164	match *data
165	{
166		syn::Data::Struct(ref data) =>
167		{
168			match data.fields
169			{
170				syn::Fields::Named(ref fields) => 
171				{
172					//An struct with named fields. Blah{name1:type1, name2:type2, ...}
173					let fit=fields.named.iter().map(|field|{
174						let name=&field.ident;
175						quote_spanned!{ field.span() =>
176							crate::quantify::Quantifiable::total_memory(&self.#name)
177						}
178					});
179					quote!{ 0 #(+ #fit)* }
180				}
181				syn::Fields::Unnamed(ref fields) =>
182				{
183					let fit=fields.unnamed.iter().enumerate().map(|(i,field)|{
184						let index=syn::Index::from(i);
185						quote_spanned!{ field.span() =>
186							crate::quantify::Quantifiable::total_memory(&self.#index)
187						}
188					});
189					quote!{ 0 #(+ #fit)* }
190				}
191				syn::Fields::Unit => quote!(0),
192			}
193		},
194		syn::Data::Enum(ref data) =>
195		{
196			//https://docs.rs/syn/1.0.44/syn/struct.DataEnum.html
197			let vit = data.variants.iter().map(|variant|{
198				let vname = &variant.ident;
199				match variant.fields
200				{
201					syn::Fields::Named(ref fields) => 
202					{
203						//An struct with named fields. Blah{name1:type1, name2:type2, ...}
204						let fit=fields.named.iter().map(|field|{
205							let name=&field.ident;
206							quote_spanned!{ field.span() =>
207								crate::quantify::Quantifiable::total_memory(#name)
208							}
209						});
210						let sum =quote!{ 0 #(+ #fit)* };
211						let fit=fields.named.iter().map(|field|{
212							let name=&field.ident;
213							quote_spanned!{ field.span() =>
214								#name
215							}
216						});
217						let args = quote!{ #(#fit,)* };
218						quote_spanned!{ variant.span() =>
219							Self::#vname{#args} => #sum,
220						}
221					},
222					syn::Fields::Unnamed(ref fields) =>
223					{
224						let fit=fields.unnamed.iter().enumerate().map(|(i,field)|{
225							//let index=syn::Index::from(i);
226							let arg=quote::format_ident!("u{}",i);
227							quote_spanned!{ field.span() =>
228								crate::quantify::Quantifiable::total_memory(#arg)
229							}
230						});
231						let sum = quote!{ 0 #(+ #fit)* };
232						let fit=fields.unnamed.iter().enumerate().map(|(i,field)|{
233							//let index=syn::Index::from(i);
234							let arg=quote::format_ident!("u{}",i);
235							quote_spanned!{ field.span() =>
236								#arg
237							}
238						});
239						let args = quote!{ #(#fit,)* };
240						quote_spanned!{ variant.span() =>
241							Self::#vname(#args) => #sum,
242						}
243					},
244					syn::Fields::Unit =>
245					{
246						//quote!(0),
247						quote!(Self::#vname => 0,)
248					},
249				}
250			});
251			//FIXME: should be max instead of plus.
252			let tokens=quote!{ match self { #( #vit)* } };
253			//quote!{compile_error!(stringify!{#tokens});#tokens}
254			tokens
255		},
256		syn::Data::Union(_) => unimplemented!(),
257	}
258}
259
260fn quantifiable_forecast_total_memory_expression(data: &syn::Data) -> proc_macro2::TokenStream
261{
262	match *data
263	{
264		syn::Data::Struct(ref data) =>
265		{
266			match data.fields
267			{
268				syn::Fields::Named(ref fields) => 
269				{
270					//An struct with named fields. Blah{name1:type1, name2:type2, ...}
271					let fit=fields.named.iter().map(|field|{
272						let name=&field.ident;
273						quote_spanned!{ field.span() =>
274							crate::quantify::Quantifiable::forecast_total_memory(&self.#name)
275						}
276					});
277					quote!{ 0 #(+ #fit)* }
278				}
279				syn::Fields::Unnamed(ref fields) =>
280				{
281					let fit=fields.unnamed.iter().enumerate().map(|(i,field)|{
282						let index=syn::Index::from(i);
283						quote_spanned!{ field.span() =>
284							crate::quantify::Quantifiable::forecast_total_memory(&self.#index)
285						}
286					});
287					quote!{ 0 #(+ #fit)* }
288				}
289				syn::Fields::Unit => quote!(0),
290			}
291		},
292		syn::Data::Enum(ref data) =>
293		{
294			//https://docs.rs/syn/1.0.44/syn/struct.DataEnum.html
295			let vit = data.variants.iter().map(|variant|{
296				let vname = &variant.ident;
297				match variant.fields
298				{
299					syn::Fields::Named(ref fields) => 
300					{
301						//An struct with named fields. Blah{name1:type1, name2:type2, ...}
302						let fit=fields.named.iter().map(|field|{
303							let name=&field.ident;
304							quote_spanned!{ field.span() =>
305								crate::quantify::Quantifiable::total_memory(#name)
306							}
307						});
308						let sum =quote!{ 0 #(+ #fit)* };
309						let fit=fields.named.iter().map(|field|{
310							let name=&field.ident;
311							quote_spanned!{ field.span() =>
312								#name
313							}
314						});
315						let args = quote!{ #(#fit,)* };
316						quote_spanned!{ variant.span() =>
317							Self::#vname{#args} => #sum,
318						}
319					},
320					syn::Fields::Unnamed(ref fields) =>
321					{
322						let fit=fields.unnamed.iter().enumerate().map(|(i,field)|{
323							//let index=syn::Index::from(i);
324							let arg=quote::format_ident!("u{}",i);
325							quote_spanned!{ field.span() =>
326								crate::quantify::Quantifiable::total_memory(#arg)
327							}
328						});
329						let sum = quote!{ 0 #(+ #fit)* };
330						let fit=fields.unnamed.iter().enumerate().map(|(i,field)|{
331							//let index=syn::Index::from(i);
332							let arg=quote::format_ident!("u{}",i);
333							quote_spanned!{ field.span() =>
334								#arg
335							}
336						});
337						let args = quote!{ #(#fit,)* };
338						quote_spanned!{ variant.span() =>
339							Self::#vname(#args) => #sum,
340						}
341					},
342					syn::Fields::Unit =>
343					{
344						//quote!(0),
345						quote!(Self::#vname => 0,)
346					},
347				}
348			});
349			//FIXME: should be max instead of plus.
350			let tokens=quote!{ match self { #( #vit)* } };
351			//quote!{compile_error!(stringify!{#tokens});#tokens}
352			tokens
353		},
354		syn::Data::Union(_) => unimplemented!(),
355	}
356}
357
358#[cfg(test)]
359mod tests {
360    #[test]
361    fn it_works() {
362        assert_eq!(2 + 2, 4);
363    }
364}