11use proc_macro2:: TokenStream ;
2- use syn:: { DeriveInput , Path } ;
2+ use syn:: { DeriveInput , Expr , Path , Type } ;
33
4+ use attrs:: AttributeSpanWrapper ;
45use field:: Field ;
56use model:: Model ;
67use util:: { inner_of_option_ty, is_option_ty, wrap_in_dummy_mod} ;
@@ -37,58 +38,105 @@ pub fn derive(item: DeriveInput) -> TokenStream {
3738 impl_generics. params . push ( parse_quote ! ( ' update) ) ;
3839 let ( impl_generics, _, _) = impl_generics. split_for_impl ( ) ;
3940
40- let ref_changeset_ty = fields_for_update. iter ( ) . map ( |field| {
41- field_changeset_ty (
42- field,
43- & table_name,
44- treat_none_as_null,
45- Some ( quote ! ( & ' update) ) ,
46- )
47- } ) ;
48- let ref_changeset_expr = fields_for_update
49- . iter ( )
50- . map ( |field| field_changeset_expr ( field, & table_name, treat_none_as_null, Some ( quote ! ( & ) ) ) ) ;
51-
52- let direct_changeset_ty = fields_for_update
53- . iter ( )
54- . map ( |field| field_changeset_ty ( field, & table_name, treat_none_as_null, None ) ) ;
55- let direct_changeset_expr = fields_for_update
56- . iter ( )
57- . map ( |field| field_changeset_expr ( field, & table_name, treat_none_as_null, None ) ) ;
58-
59- wrap_in_dummy_mod ( quote ! (
60- use diesel:: query_builder:: AsChangeset ;
61- use diesel:: prelude:: * ;
62-
63- impl #impl_generics AsChangeset for & ' update #struct_name #ty_generics
64- #where_clause
65- {
66- type Target = #table_name:: table;
67- type Changeset = <( #( #ref_changeset_ty, ) * ) as AsChangeset >:: Changeset ;
68-
69- fn as_changeset( self ) -> Self :: Changeset {
70- ( #( #ref_changeset_expr, ) * ) . as_changeset( )
41+ let mut generate_borrowed_changeset = true ;
42+
43+ let mut direct_field_ty = Vec :: with_capacity ( fields_for_update. len ( ) ) ;
44+ let mut direct_field_assign = Vec :: with_capacity ( fields_for_update. len ( ) ) ;
45+ let mut ref_field_ty = Vec :: with_capacity ( fields_for_update. len ( ) ) ;
46+ let mut ref_field_assign = Vec :: with_capacity ( fields_for_update. len ( ) ) ;
47+
48+ for field in fields_for_update {
49+ match field. serialize_as . as_ref ( ) {
50+ Some ( AttributeSpanWrapper { item : ty, .. } ) => {
51+ direct_field_ty. push ( field_changeset_ty_serialize_as (
52+ field,
53+ table_name,
54+ ty,
55+ treat_none_as_null,
56+ ) ) ;
57+ direct_field_assign. push ( field_changeset_expr_serialize_as (
58+ field,
59+ table_name,
60+ ty,
61+ treat_none_as_null,
62+ ) ) ;
63+
64+ generate_borrowed_changeset = false ; // as soon as we hit one field with #[diesel(serialize_as)] there is no point in generating the impl of AsChangeset for borrowed structs
65+ }
66+ None => {
67+ direct_field_ty. push ( field_changeset_ty (
68+ field,
69+ table_name,
70+ None ,
71+ treat_none_as_null,
72+ ) ) ;
73+ direct_field_assign. push ( field_changeset_expr (
74+ field,
75+ table_name,
76+ None ,
77+ treat_none_as_null,
78+ ) ) ;
79+ ref_field_ty. push ( field_changeset_ty (
80+ field,
81+ table_name,
82+ Some ( quote ! ( & ' update) ) ,
83+ treat_none_as_null,
84+ ) ) ;
85+ ref_field_assign. push ( field_changeset_expr (
86+ field,
87+ table_name,
88+ Some ( quote ! ( & ) ) ,
89+ treat_none_as_null,
90+ ) ) ;
7191 }
7292 }
93+ }
7394
95+ let changeset_owned = quote ! {
7496 impl #impl_generics AsChangeset for #struct_name #ty_generics
7597 #where_clause
7698 {
7799 type Target = #table_name:: table;
78- type Changeset = <( #( #direct_changeset_ty , ) * ) as AsChangeset >:: Changeset ;
100+ type Changeset = <( #( #direct_field_ty , ) * ) as AsChangeset >:: Changeset ;
79101
80102 fn as_changeset( self ) -> Self :: Changeset {
81- ( #( #direct_changeset_expr , ) * ) . as_changeset( )
103+ ( #( #direct_field_assign , ) * ) . as_changeset( )
82104 }
83105 }
106+ } ;
107+
108+ let changeset_borrowed = if generate_borrowed_changeset {
109+ quote ! {
110+ impl #impl_generics AsChangeset for & ' update #struct_name #ty_generics
111+ #where_clause
112+ {
113+ type Target = #table_name:: table;
114+ type Changeset = <( #( #ref_field_ty, ) * ) as AsChangeset >:: Changeset ;
115+
116+ fn as_changeset( self ) -> Self :: Changeset {
117+ ( #( #ref_field_assign, ) * ) . as_changeset( )
118+ }
119+ }
120+ }
121+ } else {
122+ quote ! { }
123+ } ;
124+
125+ wrap_in_dummy_mod ( quote ! (
126+ use diesel:: query_builder:: AsChangeset ;
127+ use diesel:: prelude:: * ;
128+
129+ #changeset_owned
130+
131+ #changeset_borrowed
84132 ) )
85133}
86134
87135fn field_changeset_ty (
88136 field : & Field ,
89137 table_name : & Path ,
90- treat_none_as_null : bool ,
91138 lifetime : Option < TokenStream > ,
139+ treat_none_as_null : bool ,
92140) -> TokenStream {
93141 let column_name = field. column_name ( ) ;
94142 if !treat_none_as_null && is_option_ty ( & field. ty ) {
@@ -103,8 +151,8 @@ fn field_changeset_ty(
103151fn field_changeset_expr (
104152 field : & Field ,
105153 table_name : & Path ,
106- treat_none_as_null : bool ,
107154 lifetime : Option < TokenStream > ,
155+ treat_none_as_null : bool ,
108156) -> TokenStream {
109157 let field_name = & field. name ;
110158 let column_name = field. column_name ( ) ;
@@ -118,3 +166,34 @@ fn field_changeset_expr(
118166 quote ! ( #table_name:: #column_name. eq( #lifetime self . #field_name) )
119167 }
120168}
169+
170+ fn field_changeset_ty_serialize_as (
171+ field : & Field ,
172+ table_name : & Path ,
173+ ty : & Type ,
174+ treat_none_as_null : bool ,
175+ ) -> TokenStream {
176+ let column_name = field. column_name ( ) ;
177+ if !treat_none_as_null && is_option_ty ( & field. ty ) {
178+ let inner_ty = inner_of_option_ty ( ty) ;
179+ quote ! ( std:: option:: Option <diesel:: dsl:: Eq <#table_name:: #column_name, #inner_ty>>)
180+ } else {
181+ quote ! ( diesel:: dsl:: Eq <#table_name:: #column_name, #ty>)
182+ }
183+ }
184+
185+ fn field_changeset_expr_serialize_as (
186+ field : & Field ,
187+ table_name : & Path ,
188+ ty : & Type ,
189+ treat_none_as_null : bool ,
190+ ) -> TokenStream {
191+ let field_name = & field. name ;
192+ let column_name = field. column_name ( ) ;
193+ let column: Expr = parse_quote ! ( #table_name:: #column_name) ;
194+ if !treat_none_as_null && is_option_ty ( & field. ty ) {
195+ quote ! ( self . #field_name. map( |x| #column. eq( :: std:: convert:: Into :: <#ty>:: into( x) ) ) )
196+ } else {
197+ quote ! ( #column. eq( :: std:: convert:: Into :: <#ty>:: into( self . #field_name) ) )
198+ }
199+ }
0 commit comments