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))
}
}