From fbe7ca4162fb0cd8faabfb3966a623d997220052 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Fri, 2 Feb 2018 16:01:33 -0700 Subject: [PATCH] Rewrite `#[derive(Queryable)]` in derives2 This was fairly recently rewritten, so it should in theory be the most straightforward derive to port. Unfortunately, due to https://github.com/rust-lang/rust/issues/47311, it's obnoxiously hard to actually construct a struct in a derive right now. We have to do hacky workarounds until that is fixed. --- diesel_derives/src/lib.rs | 6 -- diesel_derives/src/queryable.rs | 49 ---------------- diesel_derives/tests/tests.rs | 1 - diesel_derives2/src/field.rs | 13 +++++ diesel_derives2/src/lib.rs | 6 ++ diesel_derives2/src/queryable.rs | 57 +++++++++++++++++++ .../tests/queryable.rs | 4 +- diesel_derives2/tests/tests.rs | 1 + 8 files changed, 79 insertions(+), 58 deletions(-) delete mode 100644 diesel_derives/src/queryable.rs create mode 100644 diesel_derives2/src/queryable.rs rename {diesel_derives => diesel_derives2}/tests/queryable.rs (90%) diff --git a/diesel_derives/src/lib.rs b/diesel_derives/src/lib.rs index f149bdb28528..95877b7629d6 100644 --- a/diesel_derives/src/lib.rs +++ b/diesel_derives/src/lib.rs @@ -32,7 +32,6 @@ mod from_sql_row; mod insertable; mod model; mod query_id; -mod queryable; mod queryable_by_name; mod sql_type; mod util; @@ -40,11 +39,6 @@ mod util; use proc_macro::TokenStream; use syn::parse_derive_input; -#[proc_macro_derive(Queryable, attributes(column_name))] -pub fn derive_queryable(input: TokenStream) -> TokenStream { - expand_derive(input, queryable::derive_queryable) -} - #[proc_macro_derive(QueryableByName, attributes(table_name, column_name, sql_type, diesel))] pub fn derive_queryable_by_name(input: TokenStream) -> TokenStream { expand_derive(input, queryable_by_name::derive) diff --git a/diesel_derives/src/queryable.rs b/diesel_derives/src/queryable.rs deleted file mode 100644 index 787e4c44db4d..000000000000 --- a/diesel_derives/src/queryable.rs +++ /dev/null @@ -1,49 +0,0 @@ -use quote::Tokens; -use syn; - -use model::Model; -use util::wrap_item_in_const; - -pub fn derive_queryable(item: syn::DeriveInput) -> Tokens { - let model = t!(Model::from_item(&item, "Queryable")); - - let generics = syn::aster::from_generics(model.generics.clone()) - .ty_param_id("__DB") - .ty_param_id("__ST") - .build(); - let struct_ty = &model.ty; - - let row_ty = model.attrs.iter().map(|a| &a.ty); - let row_ty = quote!((#(#row_ty,)*)); - - let build_expr = build_expr_for_model(&model); - - wrap_item_in_const( - model.dummy_const_name("QUERYABLE"), - quote!( - impl#generics diesel::Queryable<__ST, __DB> for #struct_ty where - __DB: diesel::backend::Backend, - #row_ty: diesel::Queryable<__ST, __DB>, - { - type Row = <#row_ty as diesel::Queryable<__ST, __DB>>::Row; - - fn build(row: Self::Row) -> Self { - let row: #row_ty = diesel::Queryable::build(row); - #build_expr - } - } - ), - ) -} - -fn build_expr_for_model(model: &Model) -> Tokens { - let attr_exprs = model.attrs.iter().map(|attr| { - let name = attr.field_name(); - let idx = &attr.field_position; - quote!(#name: row.#idx) - }); - - quote!(Self { - #(#attr_exprs,)* - }) -} diff --git a/diesel_derives/tests/tests.rs b/diesel_derives/tests/tests.rs index 5d8b040b5e47..ff47dfcfd4b7 100644 --- a/diesel_derives/tests/tests.rs +++ b/diesel_derives/tests/tests.rs @@ -5,7 +5,6 @@ extern crate diesel; #[macro_use] extern crate diesel_derives; -mod queryable; mod queryable_by_name; mod associations; mod test_helpers; diff --git a/diesel_derives2/src/field.rs b/diesel_derives2/src/field.rs index df9ac47c81d9..cc021e375884 100644 --- a/diesel_derives2/src/field.rs +++ b/diesel_derives2/src/field.rs @@ -54,6 +54,19 @@ pub enum FieldName { Unnamed(syn::Index), } +impl FieldName { + pub fn for_assignment(&self) -> quote::Tokens { + match *self { + FieldName::Named(mut x) => { + // https://github.com/rust-lang/rust/issues/47311 + x.span = Span::call_site(); + quote!(#x) + } + FieldName::Unnamed(ref x) => quote!(#x), + } + } +} + impl quote::ToTokens for FieldName { fn to_tokens(&self, tokens: &mut quote::Tokens) { use proc_macro2::{Spacing, TokenNode, TokenTree}; diff --git a/diesel_derives2/src/lib.rs b/diesel_derives2/src/lib.rs index e1241cf857bf..0cd295c48d1b 100644 --- a/diesel_derives2/src/lib.rs +++ b/diesel_derives2/src/lib.rs @@ -28,6 +28,7 @@ mod util; mod as_changeset; mod identifiable; +mod queryable; use diagnostic_shim::*; @@ -42,6 +43,11 @@ pub fn derive_identifiable(input: TokenStream) -> TokenStream { expand_derive(input, identifiable::derive) } +#[proc_macro_derive(Queryable, attributes(column_name))] +pub fn derive_queryable(input: TokenStream) -> TokenStream { + expand_derive(input, queryable::derive) +} + fn expand_derive( input: TokenStream, f: fn(syn::DeriveInput) -> Result, diff --git a/diesel_derives2/src/queryable.rs b/diesel_derives2/src/queryable.rs new file mode 100644 index 000000000000..f6c2601d486e --- /dev/null +++ b/diesel_derives2/src/queryable.rs @@ -0,0 +1,57 @@ +use proc_macro2::Span; +use quote; +use syn; + +use model::*; +use util::*; + +pub fn derive(item: syn::DeriveInput) -> Result { + let model = Model::from_item(&item)?; + + let struct_name = item.ident; + let field_ty = model.fields().iter().map(|f| &f.ty).collect::>(); + let field_ty = &field_ty; + let build_expr = model.fields().iter().enumerate().map(|(i, f)| { + let field_name = &f.name.for_assignment(); + let i: syn::Index = i.into(); + // Make sure `row` has a `def_site` span + let row = quote!(row); + // https://github.com/rust-lang/rust/issues/47311 + let span = Span::call_site(); + quote_spanned!(span=> #field_name: (#row.#i)) + }); + + let (_, ty_generics, _) = item.generics.split_for_impl(); + let mut generics = item.generics.clone(); + generics + .params + .push(parse_quote!(__DB: diesel::backend::Backend)); + generics.params.push(parse_quote!(__ST)); + { + let where_clause = generics.where_clause.get_or_insert(parse_quote!(where)); + where_clause + .predicates + .push(parse_quote!((#(#field_ty,)*): Queryable<__ST, __DB>)); + } + let (impl_generics, _, where_clause) = generics.split_for_impl(); + + Ok(wrap_in_dummy_mod( + model.dummy_mod_name("queryable"), + quote! { + use self::diesel::Queryable; + + impl #impl_generics Queryable<__ST, __DB> for #struct_name #ty_generics + #where_clause + { + type Row = <(#(#field_ty,)*) as Queryable<__ST, __DB>>::Row; + + fn build(row: Self::Row) -> Self { + let row: (#(#field_ty,)*) = Queryable::build(row); + Self { + #(#build_expr,)* + } + } + } + }, + )) +} diff --git a/diesel_derives/tests/queryable.rs b/diesel_derives2/tests/queryable.rs similarity index 90% rename from diesel_derives/tests/queryable.rs rename to diesel_derives2/tests/queryable.rs index 2567daeabbce..e6cafdbb830e 100644 --- a/diesel_derives/tests/queryable.rs +++ b/diesel_derives2/tests/queryable.rs @@ -2,7 +2,7 @@ use diesel::dsl::sql; use diesel::*; use diesel::sql_types::Integer; -use test_helpers::connection; +use helpers::connection; #[test] fn named_struct_definition() { @@ -20,7 +20,7 @@ fn named_struct_definition() { #[test] fn tuple_struct() { #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable)] - struct MyStruct(#[column_name(foo)] i32, #[column_name(bar)] i32); + struct MyStruct(#[column_name = "foo"] i32, #[column_name = "bar"] i32); let conn = connection(); let data = select(sql::<(Integer, Integer)>("1, 2")).get_result(&conn); diff --git a/diesel_derives2/tests/tests.rs b/diesel_derives2/tests/tests.rs index e6ec84be39fc..5f4c44f6dbf5 100644 --- a/diesel_derives2/tests/tests.rs +++ b/diesel_derives2/tests/tests.rs @@ -12,3 +12,4 @@ mod schema; mod as_changeset; mod identifiable; +mod queryable;