#![cfg_attr(docs_rs_workaround, feature(proc_macro))]
extern crate proc_macro;
extern crate proc_macro2;
#[macro_use]
extern crate syn;
#[macro_use]
extern crate quote;
use proc_macro2::{Ident, Span};
use std::collections::HashSet;
use syn::{
parse::{Parse, ParseStream, Result},
punctuated::Punctuated,
ItemStruct,
};
struct Args {
types: HashSet<syn::Path>,
}
impl Parse for Args {
fn parse(input: ParseStream) -> Result<Self> {
let types = Punctuated::<syn::Path, Token![,]>::parse_terminated(input)?;
Ok(Args {
types: types.into_iter().collect(),
})
}
}
#[proc_macro_attribute]
pub fn vectorize_struct(types: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let args: Args = syn::parse(types).unwrap();
let strct: ItemStruct = syn::parse(input).unwrap();
let mut mod_name = String::from("vectorized_");
mod_name.push_str(&strct.ident.to_string());
let mod_ident = Ident::new(&mod_name, Span::call_site());
let struct_ident = &strct.ident;
let enum_members: &Vec<_> = &strct
.fields
.iter()
.map(|f| {
let ident = f.ident.as_ref().unwrap();
quote!(#ident)
}).collect();
let try_gets: &Vec<_> = &args
.types
.iter()
.map(|t| {
quote!(
impl<'a, T : #t> TryGet<'a, &'a #t> for T {
fn get(&'a self) -> Option<&'a #t>{
Some(self)
}
}
impl<'a, T> TryGet<'a, &'a #t> for T {
default fn get(&'a self) -> Option<&'a #t>{
None
}
}
)
}).collect();
let vector_members: &Vec<_> = &strct
.fields
.iter()
.map(|f| {
let ident = f.ident.as_ref().unwrap();
quote!((Fields::#ident,self.#ident.get()))
}).collect();
let vectorizes = args.types.iter().map(move |t| {
quote!(
impl<'a> Vectorize<'a, &'a #t> for #struct_ident {
fn vectorize(&'a self) -> Vec<(Fields,Option<&'a #t>)>{
vec![#(#vector_members,)*]
}
}
)
});
let result = quote!(
#strct
mod #mod_ident {
use super::*;
pub trait TryGet<'a, G> {
fn get(&'a self) -> Option<G>;
}
#(#try_gets )*
pub trait Vectorize<'a, G> {
fn vectorize(&'a self) -> Vec<(Fields, Option<G>)>;
}
#(#vectorizes )*
#[allow(bad_style)]
#[derive(Debug)]
pub enum Fields {
#(#enum_members ,)*
}
}
).into();
result
}