blob: ef9a44f6fe2d6e48f8a4a1330ec092f154774181 [file] [log] [blame]
use proc_macro2::{self, Ident, Span};
use syn;
use field::*;
use model::*;
use util::*;
pub fn derive(item: syn::DeriveInput) -> Result<proc_macro2::TokenStream, Diagnostic> {
let model = Model::from_item(&item)?;
let struct_name = &item.ident;
let field_expr = model
.fields()
.iter()
.map(|f| field_expr(f, &model))
.collect::<Result<Vec<_>, _>>()?;
let (_, ty_generics, ..) = item.generics.split_for_impl();
let mut generics = item.generics.clone();
generics
.params
.push(parse_quote!(__DB: diesel::backend::Backend));
for field in model.fields() {
let where_clause = generics.where_clause.get_or_insert(parse_quote!(where));
let field_ty = field.ty_for_deserialize()?;
if field.has_flag("embed") {
where_clause
.predicates
.push(parse_quote!(#field_ty: QueryableByName<__DB>));
} else {
let st = sql_type(field, &model);
where_clause
.predicates
.push(parse_quote!(#field_ty: diesel::deserialize::FromSql<#st, __DB>));
}
}
let (impl_generics, _, where_clause) = generics.split_for_impl();
Ok(wrap_in_dummy_mod(quote! {
use diesel::deserialize::{self, QueryableByName};
use diesel::row::NamedRow;
impl #impl_generics QueryableByName<__DB>
for #struct_name #ty_generics
#where_clause
{
fn build<__R: NamedRow<__DB>>(row: &__R) -> deserialize::Result<Self> {
std::result::Result::Ok(Self {
#(#field_expr,)*
})
}
}
}))
}
fn field_expr(field: &Field, model: &Model) -> Result<syn::FieldValue, Diagnostic> {
if field.has_flag("embed") {
Ok(field
.name
.assign(parse_quote!(QueryableByName::build(row)?)))
} else {
let column_name = field.column_name();
let ty = field.ty_for_deserialize()?;
let st = sql_type(field, model);
Ok(field
.name
.assign(parse_quote!(row.get::<#st, #ty>(stringify!(#column_name))?.into())))
}
}
fn sql_type(field: &Field, model: &Model) -> syn::Type {
let table_name = model.table_name();
let column_name = field.column_name();
match field.sql_type {
Some(ref st) => st.clone(),
None => {
if model.has_table_name_attribute() {
parse_quote!(diesel::dsl::SqlTypeOf<#table_name::#column_name>)
} else {
let field_name = match field.name {
FieldName::Named(ref x) => x.clone(),
_ => Ident::new("field", Span::call_site()),
};
field
.span
.error(format!("Cannot determine the SQL type of {}", field_name))
.help(
"Your struct must either be annotated with `#[table_name = \"foo\"]` \
or have all of its fields annotated with `#[sql_type = \"Integer\"]`",
)
.emit();
parse_quote!(())
}
}
}
}