Skip to content

Commit 33856d0

Browse files
authored
Merge pull request diesel-rs#1570 from weiznich/feature/as_changeset_on_struct
Derive `AsChangeset` also for the plain struct, similar to `Insertable`
2 parents a2b45a1 + 424a738 commit 33856d0

3 files changed

Lines changed: 68 additions & 11 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/
6868

6969
* Columns generated by `table!` now implement `Default`
7070

71+
* `#[derive(AsChangeset)]` now implements `AsChangeset` on the struct itself,
72+
and not only on a reference to the struct
73+
7174
### Changed
7275

7376
* The bounds on `impl ToSql for Cow<'a, T>` have been loosened to no longer

diesel_derives/src/as_changeset.rs

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,23 @@ pub fn derive(item: syn::DeriveInput) -> Result<quote::Tokens, Diagnostic> {
3030
.iter()
3131
.filter(|f| !model.primary_key_names.contains(&f.column_name()))
3232
.collect::<Vec<_>>();
33-
let changeset_ty = fields_for_update
33+
let ref_changeset_ty = fields_for_update.iter().map(|field| {
34+
field_changeset_ty(
35+
field,
36+
table_name,
37+
treat_none_as_null,
38+
Some(quote!(&'update)),
39+
)
40+
});
41+
let ref_changeset_expr = fields_for_update
3442
.iter()
35-
.map(|field| field_changeset_ty(field, table_name, treat_none_as_null));
36-
let changeset_expr = fields_for_update
43+
.map(|field| field_changeset_expr(field, table_name, treat_none_as_null, Some(quote!(&))));
44+
let direct_changeset_ty = fields_for_update
3745
.iter()
38-
.map(|field| field_changeset_expr(field, table_name, treat_none_as_null));
46+
.map(|field| field_changeset_ty(field, table_name, treat_none_as_null, None));
47+
let direct_changeset_expr = fields_for_update
48+
.iter()
49+
.map(|field| field_changeset_expr(field, table_name, treat_none_as_null, None));
3950

4051
if fields_for_update.is_empty() {
4152
Span::call_site()
@@ -57,10 +68,21 @@ pub fn derive(item: syn::DeriveInput) -> Result<quote::Tokens, Diagnostic> {
5768
#where_clause
5869
{
5970
type Target = #table_name::table;
60-
type Changeset = <(#(#changeset_ty,)*) as AsChangeset>::Changeset;
71+
type Changeset = <(#(#ref_changeset_ty,)*) as AsChangeset>::Changeset;
72+
73+
fn as_changeset(self) -> Self::Changeset {
74+
(#(#ref_changeset_expr,)*).as_changeset()
75+
}
76+
}
77+
78+
impl #impl_generics AsChangeset for #struct_name #ty_generics
79+
#where_clause
80+
{
81+
type Target = #table_name::table;
82+
type Changeset = <(#(#direct_changeset_ty,)*) as AsChangeset>::Changeset;
6183

6284
fn as_changeset(self) -> Self::Changeset {
63-
(#(#changeset_expr,)*).as_changeset()
85+
(#(#direct_changeset_expr,)*).as_changeset()
6486
}
6587
}
6688
),
@@ -71,27 +93,33 @@ fn field_changeset_ty(
7193
field: &Field,
7294
table_name: syn::Ident,
7395
treat_none_as_null: bool,
96+
lifetime: Option<quote::Tokens>,
7497
) -> syn::Type {
7598
let column_name = field.column_name();
7699
if !treat_none_as_null && is_option_ty(&field.ty) {
77100
let field_ty = inner_of_option_ty(&field.ty);
78-
parse_quote!(std::option::Option<diesel::dsl::Eq<#table_name::#column_name, &'update #field_ty>>)
101+
parse_quote!(std::option::Option<diesel::dsl::Eq<#table_name::#column_name, #lifetime #field_ty>>)
79102
} else {
80103
let field_ty = &field.ty;
81-
parse_quote!(diesel::dsl::Eq<#table_name::#column_name, &'update #field_ty>)
104+
parse_quote!(diesel::dsl::Eq<#table_name::#column_name, #lifetime #field_ty>)
82105
}
83106
}
84107

85108
fn field_changeset_expr(
86109
field: &Field,
87110
table_name: syn::Ident,
88111
treat_none_as_null: bool,
112+
lifetime: Option<quote::Tokens>,
89113
) -> syn::Expr {
90114
let field_access = field.name.access();
91115
let column_name = field.column_name();
92116
if !treat_none_as_null && is_option_ty(&field.ty) {
93-
parse_quote!(self#field_access.as_ref().map(|x| #table_name::#column_name.eq(x)))
117+
if lifetime.is_some() {
118+
parse_quote!(self#field_access.as_ref().map(|x| #table_name::#column_name.eq(x)))
119+
} else {
120+
parse_quote!(self#field_access.map(|x| #table_name::#column_name.eq(x)))
121+
}
94122
} else {
95-
parse_quote!(#table_name::#column_name.eq(&self#field_access))
123+
parse_quote!(#table_name::#column_name.eq(#lifetime self#field_access))
96124
}
97125
}

diesel_derives/tests/as_changeset.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use helpers::*;
33
use schema::*;
44

55
#[test]
6-
fn named_struct() {
6+
fn named_ref_struct() {
77
#[derive(AsChangeset)]
88
struct User {
99
name: String,
@@ -28,6 +28,32 @@ fn named_struct() {
2828
assert_eq!(Ok(expected), actual);
2929
}
3030

31+
#[test]
32+
fn named_struct() {
33+
#[derive(AsChangeset)]
34+
struct User {
35+
name: String,
36+
hair_color: String,
37+
}
38+
39+
let connection = connection_with_sean_and_tess_in_users_table();
40+
41+
update(users::table.find(1))
42+
.set(User {
43+
name: String::from("Jim"),
44+
hair_color: String::from("blue"),
45+
})
46+
.execute(&connection)
47+
.unwrap();
48+
49+
let expected = vec![
50+
(1, String::from("Jim"), Some(String::from("blue"))),
51+
(2, String::from("Tess"), Some(String::from("brown"))),
52+
];
53+
let actual = users::table.order(users::id).load(&connection);
54+
assert_eq!(Ok(expected), actual);
55+
}
56+
3157
#[test]
3258
fn with_explicit_table_name() {
3359
#[derive(AsChangeset)]

0 commit comments

Comments
 (0)