Skip to content

Commit

Permalink
Fix #[changeset_for] with single field structs
Browse files Browse the repository at this point in the history
This error occurred because we were always assuming the type and body to
work with tuples, and `Changeset` isn't implemented for `(A)`. We do the
same workaround in the generation for `insertable_into`. I'd rather just
add `(A)` to the tuple impls, but this is unfortunately impossible due
to rust-lang/rust#30762
  • Loading branch information
sgrif committed Jan 7, 2016
1 parent 2c278f2 commit cf56133
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 7 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/
* No longer generate invalid SQL when an optional update field is not the first
field on a changeset. Fixes [#68](https://github.com/sgrif/diesel/issues/68).

* `#[changeset_for]` can now be used with structs containing only a single field
other than `id`. Fixes [#66](https://github.com/sgrif/diesel/issues/66).

## [0.3.0] 2015-12-04

### Changed
Expand Down
23 changes: 16 additions & 7 deletions diesel_codegen/src/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,26 @@ fn changeset_impl(
let pk = model.primary_key_name();
let attrs_for_changeset = model.attrs.iter().filter(|a| a.column_name != pk)
.collect::<Vec<_>>();
let changeset_ty = builder.ty().tuple()
.with_tys(attrs_for_changeset.iter().map(|a| changeset_ty(cx, builder, table, a)))
.build();
let changeset_body = builder.expr().tuple()
.with_exprs(attrs_for_changeset.iter().map(|a| changeset_expr(cx, builder, table, a)))
.build();
let changeset_t;
let changeset_body;
if attrs_for_changeset.len() == 1 {
changeset_t = changeset_ty(cx, builder, table, attrs_for_changeset[0]);
changeset_body = changeset_expr(cx, builder, table, attrs_for_changeset[0]);
} else {
changeset_t = builder.ty().tuple()
.with_tys(attrs_for_changeset.iter()
.map(|a| changeset_ty(cx, builder, table, a)))
.build();
changeset_body = builder.expr().tuple()
.with_exprs(attrs_for_changeset.iter()
.map(|a| changeset_expr(cx, builder, table, a)))
.build();
}
quote_item!(cx,
impl<'a: 'update, 'update> ::diesel::query_builder::AsChangeset for
&'update $struct_name
{
type Changeset = $changeset_ty;
type Changeset = $changeset_t;

fn as_changeset(self) -> Self::Changeset {
$changeset_body
Expand Down
22 changes: 22 additions & 0 deletions diesel_tests/tests/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,25 @@ fn sql_syntax_is_correct_when_option_field_comes_mixed_with_non_option() {
let expected_post = Post::new(post.id, sean.id, "Hello".into(), Some("earth".into()));
assert_eq!(expected_post, post);
}

#[test]
fn can_update_with_struct_containing_single_field() {
#[changeset_for(posts)]
struct SetBody {
body: String,
}

let connection = connection_with_sean_and_tess_in_users_table();
let sean = find_user_by_name("Sean", &connection);
let new_post = sean.new_post("Hello", Some("world"));
insert(&new_post).into(posts::table).execute(&connection).unwrap();

let changes = SetBody { body: "earth".into() };
let post = update(posts::table)
.set(&changes)
.get_result::<Post>(&connection)
.unwrap();

let expected_post = Post::new(post.id, sean.id, "Hello".into(), Some("earth".into()));
assert_eq!(expected_post, post);
}

0 comments on commit cf56133

Please sign in to comment.