extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, PathArguments, Type};
#[proc_macro_derive(RowConsumer)]
pub fn derive_row_consumer(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = input.ident;
let data = input.data;
parse_field_setters(&name, &data)
}
fn parse_field_setters(class_name: &Ident, data: &Data) -> TokenStream {
match *data {
Data::Struct(ref data) => match data.fields {
Fields::Named(ref fields) => {
let field_setters = fields.named.iter().enumerate().map(|(i, f)| {
let field_name = &f.ident;
let (field_type, field_type_args) = match &f.ty {
Type::Path(field_ty) => match field_ty.path.segments.last() {
Some(path_segment) => match &path_segment.arguments {
PathArguments::AngleBracketed(previous_type_args) => {
let mut new_field_type = f.ty.clone();
match new_field_type {
Type::Path(ref mut field_ty) => match field_ty.path.segments.last_mut() {
Some(v) => {
v.arguments = PathArguments::None;
(new_field_type, Some(previous_type_args))
},
None => (f.ty.clone(), None)
},
_ => (f.ty.clone(), None),
}
},
_ => (f.ty.clone(), None),
},
None => (f.ty.clone(), None),
},
_ => (f.ty.clone(), None),
};
match field_type_args {
Some(v) => quote! {
#field_name: match row.try_get::<usize, #field_type::#v>(#i) {
Ok(v) => v,
Err(_) => {
errors.push(format!("Conversion error occurred for field \"{}\" on class \"{}\"", stringify!(#field_name), stringify!(#class_name)));
#field_type::default()
},
}
},
None => quote! {
#field_name: match row.try_get::<usize, #field_type>(#i) {
Ok(v) => v,
Err(_) => {
errors.push(format!("Conversion error occurred for field \"{}\" on class \"{}\"", stringify!(#field_name), stringify!(#class_name)));
#field_type::default()
},
}
},
}
});
let implementation = quote! {
impl pgde::RowConsumer for #class_name {
fn from_row(row: Row) -> Result<Self, (Self, Vec<String>)>
where
Self: Sized,
{
let mut errors : Vec<String> = Vec::new();
let class_instance = Self {
#(#field_setters),*
};
match errors.len() {
0 => Ok(class_instance),
_ => Err((class_instance, errors)),
}
}
}
};
TokenStream::from(implementation)
}
Fields::Unnamed(_) | Fields::Unit => panic!(
"RowConsumer is not supported on unit structs nor structs with unnamed fieds"
),
},
Data::Enum(_) | Data::Union(_) => panic!("RowConsumer is not supported on enums or unions"),
}
}