1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
use proc_macro2; use proc_macro2::Span; use syn; use diagnostic_shim::*; use field::*; use meta::*; use model::*; use util::*; pub fn derive(item: syn::DeriveInput) -> Result<proc_macro2::TokenStream, Diagnostic> { let treat_none_as_null = MetaItem::with_name(&item.attrs, "changeset_options") .map(|meta| { meta.warn_if_other_options(&["treat_none_as_null"]); meta.required_nested_item("treat_none_as_null") .map(|m| m.expect_bool_value()) }) .unwrap_or(Ok(false))?; let model = Model::from_item(&item)?; let struct_name = &model.name; let table_name = model.table_name(); let (_, ty_generics, where_clause) = item.generics.split_for_impl(); let mut impl_generics = item.generics.clone(); impl_generics.params.push(parse_quote!('update)); let (impl_generics, _, _) = impl_generics.split_for_impl(); let fields_for_update = model .fields() .iter() .filter(|f| !model.primary_key_names.contains(&f.column_name())) .collect::<Vec<_>>(); let ref_changeset_ty = fields_for_update.iter().map(|field| { field_changeset_ty( field, &table_name, treat_none_as_null, Some(quote!(&'update)), ) }); let ref_changeset_expr = fields_for_update .iter() .map(|field| field_changeset_expr(field, &table_name, treat_none_as_null, Some(quote!(&)))); let direct_changeset_ty = fields_for_update .iter() .map(|field| field_changeset_ty(field, &table_name, treat_none_as_null, None)); let direct_changeset_expr = fields_for_update .iter() .map(|field| field_changeset_expr(field, &table_name, treat_none_as_null, None)); if fields_for_update.is_empty() { Span::call_site() .error( "Deriving `AsChangeset` on a structure that only contains the primary key isn't supported." ) .help("If you want to change the primary key of a row, you should do so with `.set(table::id.eq(new_id))`.") .note("`#[derive(AsChangeset)]` never changes the primary key of a row.") .emit(); } Ok(wrap_in_dummy_mod( model.dummy_mod_name("as_changeset"), quote!( use diesel::query_builder::AsChangeset; use diesel::prelude::*; impl #impl_generics AsChangeset for &'update #struct_name #ty_generics #where_clause { type Target = #table_name::table; type Changeset = <(#(#ref_changeset_ty,)*) as AsChangeset>::Changeset; fn as_changeset(self) -> Self::Changeset { (#(#ref_changeset_expr,)*).as_changeset() } } impl #impl_generics AsChangeset for #struct_name #ty_generics #where_clause { type Target = #table_name::table; type Changeset = <(#(#direct_changeset_ty,)*) as AsChangeset>::Changeset; fn as_changeset(self) -> Self::Changeset { (#(#direct_changeset_expr,)*).as_changeset() } } ), )) } fn field_changeset_ty( field: &Field, table_name: &syn::Ident, treat_none_as_null: bool, lifetime: Option<proc_macro2::TokenStream>, ) -> syn::Type { let column_name = field.column_name(); if !treat_none_as_null && is_option_ty(&field.ty) { let field_ty = inner_of_option_ty(&field.ty); parse_quote!(std::option::Option<diesel::dsl::Eq<#table_name::#column_name, #lifetime #field_ty>>) } else { let field_ty = &field.ty; parse_quote!(diesel::dsl::Eq<#table_name::#column_name, #lifetime #field_ty>) } } fn field_changeset_expr( field: &Field, table_name: &syn::Ident, treat_none_as_null: bool, lifetime: Option<proc_macro2::TokenStream>, ) -> syn::Expr { let field_access = field.name.access(); let column_name = field.column_name(); if !treat_none_as_null && is_option_ty(&field.ty) { if lifetime.is_some() { parse_quote!(self#field_access.as_ref().map(|x| #table_name::#column_name.eq(x))) } else { parse_quote!(self#field_access.map(|x| #table_name::#column_name.eq(x))) } } else { parse_quote!(#table_name::#column_name.eq(#lifetime self#field_access)) } }