extern crate proc_macro;
extern crate quote;
extern crate syn;
extern crate synstructure;
use syn::{parse_macro_input, DeriveInput};
use syn::spanned::Spanned;
use quote::{quote, quote_spanned};
#[proc_macro_derive(Quantifiable)]
pub fn quantifiable_macro_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let total_memory_body = quantifiable_total_memory_expression(&ast.data);
let forecast_total_memory_body = quantifiable_forecast_total_memory_expression(&ast.data);
let name = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
let where_clause = where_clause.clone();
let tokens = quote! {
impl #impl_generics crate::quantify::Quantifiable for #name #ty_generics #where_clause {
#[inline]
#[allow(unused_variables, unused_mut, unreachable_code)]
fn total_memory(&self) -> usize {
#total_memory_body
}
fn print_memory_breakdown(&self)
{
unimplemented!();
}
fn forecast_total_memory(&self) -> usize
{
#forecast_total_memory_body
}
}
};
proc_macro::TokenStream::from(tokens)
}
fn quantifiable_total_memory_expression(data: &syn::Data) -> proc_macro2::TokenStream
{
match *data
{
syn::Data::Struct(ref data) =>
{
match data.fields
{
syn::Fields::Named(ref fields) =>
{
let fit=fields.named.iter().map(|field|{
let name=&field.ident;
quote_spanned!{ field.span() =>
crate::quantify::Quantifiable::total_memory(&self.#name)
}
});
quote!{ 0 #(+ #fit)* }
}
syn::Fields::Unnamed(ref fields) =>
{
let fit=fields.unnamed.iter().enumerate().map(|(i,field)|{
let index=syn::Index::from(i);
quote_spanned!{ field.span() =>
crate::quantify::Quantifiable::total_memory(&self.#index)
}
});
quote!{ 0 #(+ #fit)* }
}
syn::Fields::Unit => quote!(0),
}
},
syn::Data::Enum(ref data) =>
{
let vit = data.variants.iter().map(|variant|{
let vname = &variant.ident;
match variant.fields
{
syn::Fields::Named(ref fields) =>
{
let fit=fields.named.iter().map(|field|{
let name=&field.ident;
quote_spanned!{ field.span() =>
crate::quantify::Quantifiable::total_memory(#name)
}
});
let sum =quote!{ 0 #(+ #fit)* };
let fit=fields.named.iter().map(|field|{
let name=&field.ident;
quote_spanned!{ field.span() =>
#name
}
});
let args = quote!{ #(#fit,)* };
quote_spanned!{ variant.span() =>
Self::#vname{#args} => #sum,
}
},
syn::Fields::Unnamed(ref fields) =>
{
let fit=fields.unnamed.iter().enumerate().map(|(i,field)|{
let arg=quote::format_ident!("u{}",i);
quote_spanned!{ field.span() =>
crate::quantify::Quantifiable::total_memory(#arg)
}
});
let sum = quote!{ 0 #(+ #fit)* };
let fit=fields.unnamed.iter().enumerate().map(|(i,field)|{
let arg=quote::format_ident!("u{}",i);
quote_spanned!{ field.span() =>
#arg
}
});
let args = quote!{ #(#fit,)* };
quote_spanned!{ variant.span() =>
Self::#vname(#args) => #sum,
}
},
syn::Fields::Unit =>
{
quote!(Self::#vname => 0,)
},
}
});
let tokens=quote!{ match self { #( #vit)* } };
tokens
},
syn::Data::Union(_) => unimplemented!(),
}
}
fn quantifiable_forecast_total_memory_expression(data: &syn::Data) -> proc_macro2::TokenStream
{
match *data
{
syn::Data::Struct(ref data) =>
{
match data.fields
{
syn::Fields::Named(ref fields) =>
{
let fit=fields.named.iter().map(|field|{
let name=&field.ident;
quote_spanned!{ field.span() =>
crate::quantify::Quantifiable::forecast_total_memory(&self.#name)
}
});
quote!{ 0 #(+ #fit)* }
}
syn::Fields::Unnamed(ref fields) =>
{
let fit=fields.unnamed.iter().enumerate().map(|(i,field)|{
let index=syn::Index::from(i);
quote_spanned!{ field.span() =>
crate::quantify::Quantifiable::forecast_total_memory(&self.#index)
}
});
quote!{ 0 #(+ #fit)* }
}
syn::Fields::Unit => quote!(0),
}
},
syn::Data::Enum(ref data) =>
{
let vit = data.variants.iter().map(|variant|{
let vname = &variant.ident;
match variant.fields
{
syn::Fields::Named(ref fields) =>
{
let fit=fields.named.iter().map(|field|{
let name=&field.ident;
quote_spanned!{ field.span() =>
crate::quantify::Quantifiable::total_memory(#name)
}
});
let sum =quote!{ 0 #(+ #fit)* };
let fit=fields.named.iter().map(|field|{
let name=&field.ident;
quote_spanned!{ field.span() =>
#name
}
});
let args = quote!{ #(#fit,)* };
quote_spanned!{ variant.span() =>
Self::#vname{#args} => #sum,
}
},
syn::Fields::Unnamed(ref fields) =>
{
let fit=fields.unnamed.iter().enumerate().map(|(i,field)|{
let arg=quote::format_ident!("u{}",i);
quote_spanned!{ field.span() =>
crate::quantify::Quantifiable::total_memory(#arg)
}
});
let sum = quote!{ 0 #(+ #fit)* };
let fit=fields.unnamed.iter().enumerate().map(|(i,field)|{
let arg=quote::format_ident!("u{}",i);
quote_spanned!{ field.span() =>
#arg
}
});
let args = quote!{ #(#fit,)* };
quote_spanned!{ variant.span() =>
Self::#vname(#args) => #sum,
}
},
syn::Fields::Unit =>
{
quote!(Self::#vname => 0,)
},
}
});
let tokens=quote!{ match self { #( #vit)* } };
tokens
},
syn::Data::Union(_) => unimplemented!(),
}
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}