forked from diesel-rs/diesel
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinsertable.rs
More file actions
121 lines (106 loc) · 3.81 KB
/
Copy pathinsertable.rs
File metadata and controls
121 lines (106 loc) · 3.81 KB
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
use syn;
use quote;
use proc_macro2::Span;
use field::*;
use model::*;
use util::*;
pub fn derive(item: syn::DeriveInput) -> Result<quote::Tokens, Diagnostic> {
let model = Model::from_item(&item)?;
if model.fields().is_empty() {
return Err(Span::call_site()
.error("Cannot derive Insertable for unit structs")
.help(format!(
"Use `insert_into({}::table).default_values()` if you want `DEFAULT VALUES`",
model.table_name()
)));
}
let table_name = model.table_name();
let struct_name = item.ident;
let (_, ty_generics, where_clause) = item.generics.split_for_impl();
let mut impl_generics = item.generics.clone();
impl_generics.params.push(parse_quote!('insert));
let (impl_generics, ..) = impl_generics.split_for_impl();
let (direct_field_ty, direct_field_assign): (Vec<_>, Vec<_>) = model
.fields()
.iter()
.map(|f| {
(
(field_ty(f, table_name, None)),
(field_expr(f, table_name, None)),
)
})
.unzip();
let (ref_field_ty, ref_field_assign): (Vec<_>, Vec<_>) = model
.fields()
.iter()
.map(|f| {
(
(field_ty(f, table_name, Some(quote!(&'insert)))),
(field_expr(f, table_name, Some(quote!(&)))),
)
})
.unzip();
Ok(wrap_in_dummy_mod(
model.dummy_mod_name("insertable"),
quote! {
use self::diesel::insertable::Insertable;
use self::diesel::query_builder::UndecoratedInsertRecord;
use self::diesel::prelude::*;
impl #impl_generics Insertable<#table_name::table> for #struct_name #ty_generics
#where_clause
{
type Values = <(#(#direct_field_ty,)*) as Insertable<#table_name::table>>::Values;
fn values(self) -> Self::Values {
(#(#direct_field_assign,)*).values()
}
}
impl #impl_generics Insertable<#table_name::table>
for &'insert #struct_name #ty_generics
#where_clause
{
type Values = <(#(#ref_field_ty,)*) as Insertable<#table_name::table>>::Values;
fn values(self) -> Self::Values {
(#(#ref_field_assign,)*).values()
}
}
impl #impl_generics UndecoratedInsertRecord<#table_name::table>
for #struct_name #ty_generics
#where_clause
{
}
},
))
}
fn field_ty(field: &Field, table_name: syn::Ident, lifetime: Option<quote::Tokens>) -> syn::Type {
if field.has_flag("embed") {
let field_ty = &field.ty;
parse_quote!(#lifetime #field_ty)
} else {
let inner_ty = inner_of_option_ty(&field.ty);
let column_name = field.column_name();
parse_quote!(
std::option::Option<diesel::dsl::Eq<
#table_name::#column_name,
#lifetime #inner_ty,
>>
)
}
}
fn field_expr(field: &Field, table_name: syn::Ident, lifetime: Option<quote::Tokens>) -> syn::Expr {
let field_access = field.name.access();
if field.has_flag("embed") {
parse_quote!(#lifetime self#field_access)
} else {
let column_name = field.column_name();
let column: syn::Expr = parse_quote!(#table_name::#column_name);
if is_option_ty(&field.ty) {
if lifetime.is_some() {
parse_quote!(self#field_access.as_ref().map(|x| #column.eq(x)))
} else {
parse_quote!(self#field_access.map(|x| #column.eq(x)))
}
} else {
parse_quote!(std::option::Option::Some(#column.eq(#lifetime self#field_access)))
}
}
}