| use proc_macro2::{Span, TokenStream}; |
| use syn::{DeriveInput, Ident, LitStr, Type}; |
| |
| use attrs::AttributeSpanWrapper; |
| use field::{Field, FieldName}; |
| use model::Model; |
| use util::wrap_in_dummy_mod; |
| |
| pub fn derive(item: DeriveInput) -> TokenStream { |
| let model = Model::from_item(&item, false); |
| |
| let struct_name = &item.ident; |
| let fields = &model.fields().iter().map(get_ident).collect::<Vec<_>>(); |
| let field_names = model.fields().iter().map(|f| &f.name); |
| |
| let initial_field_expr = model.fields().iter().map(|f| { |
| let field_ty = &f.ty; |
| |
| if f.embed() { |
| quote!(<#field_ty as QueryableByName<__DB>>::build(row)?) |
| } else { |
| let deserialize_ty = f.ty_for_deserialize(); |
| let name = f.column_name(); |
| let name = LitStr::new(&name.to_string(), name.span()); |
| quote!( |
| { |
| let field = diesel::row::NamedRow::get(row, #name)?; |
| <#deserialize_ty as Into<#field_ty>>::into(field) |
| } |
| ) |
| } |
| }); |
| |
| 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.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(); |
| |
| wrap_in_dummy_mod(quote! { |
| use diesel::deserialize::{self, QueryableByName}; |
| use diesel::row::{NamedRow}; |
| use diesel::sql_types::Untyped; |
| |
| impl #impl_generics QueryableByName<__DB> |
| for #struct_name #ty_generics |
| #where_clause |
| { |
| fn build<'__a>(row: &impl NamedRow<'__a, __DB>) -> deserialize::Result<Self> |
| { |
| #( |
| let mut #fields = #initial_field_expr; |
| )* |
| deserialize::Result::Ok(Self { |
| #( |
| #field_names: #fields, |
| )* |
| }) |
| } |
| } |
| }) |
| } |
| |
| fn get_ident(field: &Field) -> Ident { |
| match &field.name { |
| FieldName::Named(n) => n.clone(), |
| FieldName::Unnamed(i) => Ident::new(&format!("field_{}", i.index), i.span), |
| } |
| } |
| |
| fn sql_type(field: &Field, model: &Model) -> Type { |
| let table_name = model.table_name(); |
| |
| match field.sql_type { |
| Some(AttributeSpanWrapper { item: ref st, .. }) => st.clone(), |
| None => { |
| if model.has_table_name_attribute() { |
| let column_name = field.column_name(); |
| 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()), |
| }; |
| abort!( |
| field.span, "Cannot determine the SQL type of {}", field_name; |
| help = "Your struct must either be annotated with `#[diesel(table_name = foo)]` or have this field annotated with `#[diesel(sql_type = ...)]`"; |
| ); |
| } |
| } |
| } |
| } |