Skip to main content

diesel_derives/
selectable.rs

1use proc_macro2::{Span, TokenStream};
2use quote::{quote, quote_spanned};
3use std::borrow::Cow;
4use syn::spanned::Spanned;
5use syn::{DeriveInput, Ident, Result, parse_quote};
6
7use crate::field::Field;
8use crate::model::Model;
9use crate::util::wrap_in_dummy_mod;
10use diesel_attribute_parser::CheckForBackend;
11
12type DefaultCheckCallback = fn(
13    &Model,
14    &syn::ImplGenerics<'_>,
15    Option<&syn::WhereClause>,
16    &[FieldSelectExpressionTyBuilder<'_>],
17) -> std::prelude::v1::Result<TokenStream, syn::Error>;
18
19pub fn derive(
20    item: DeriveInput,
21    default_check: Option<DefaultCheckCallback>,
22) -> Result<TokenStream> {
23    let model = Model::from_item(&item, false, false)?;
24
25    let (original_impl_generics, ty_generics, original_where_clause) =
26        item.generics.split_for_impl();
27
28    let mut generics = item.generics.clone();
29    generics
30        .params
31        .push(::syn::__private::parse_quote({
        let mut _s = ::quote::__private::TokenStream::new();
        ::quote::__private::push_ident(&mut _s, "__DB");
        ::quote::__private::push_colon(&mut _s);
        ::quote::__private::push_ident(&mut _s, "diesel");
        ::quote::__private::push_colon2(&mut _s);
        ::quote::__private::push_ident(&mut _s, "backend");
        ::quote::__private::push_colon2(&mut _s);
        ::quote::__private::push_ident(&mut _s, "Backend");
        _s
    })parse_quote!(__DB: diesel::backend::Backend));
32
33    for embed_field in model.fields().iter().filter(|f| f.embed()) {
34        let embed_ty = &embed_field.ty;
35        generics
36            .make_where_clause()
37            .predicates
38            .push(::syn::__private::parse_quote({
        let mut _s = ::quote::__private::TokenStream::new();
        ::quote::ToTokens::to_tokens(&embed_ty, &mut _s);
        ::quote::__private::push_colon(&mut _s);
        ::quote::__private::push_ident(&mut _s, "Selectable");
        ::quote::__private::push_lt(&mut _s);
        ::quote::__private::push_ident(&mut _s, "__DB");
        ::quote::__private::push_gt(&mut _s);
        _s
    })parse_quote!(#embed_ty: Selectable<__DB>));
39    }
40
41    let (impl_generics, _, where_clause) = generics.split_for_impl();
42
43    let struct_name = &item.ident;
44
45    let mut compile_errors: Vec<syn::Error> = Vec::new();
46    let field_select_expression_type_builders = model
47        .fields()
48        .iter()
49        .map(|f| field_select_expression_ty_builder(f, &model, &mut compile_errors))
50        .collect::<Result<Vec<_>>>()?;
51    let field_select_expression_types = field_select_expression_type_builders
52        .iter()
53        .map(|f| f.type_with_backend(&::syn::__private::parse_quote({
        let mut _s = ::quote::__private::TokenStream::new();
        ::quote::__private::push_ident(&mut _s, "__DB");
        _s
    })parse_quote!(__DB)))
54        .collect::<Vec<_>>();
55    let field_select_expressions = model
56        .fields()
57        .iter()
58        .map(|f| field_column_inst(f, &model))
59        .collect::<Result<Vec<_>>>()?;
60
61    let check_function = match model.check_for_backend.as_ref() {
62        Some(CheckForBackend::Backends(backends)) => Some(generate_check_function(
63            &model,
64            &original_impl_generics,
65            original_where_clause,
66            &field_select_expression_type_builders,
67            backends,
68            ::syn::__private::parse_quote({
        let mut _s = ::quote::__private::TokenStream::new();
        ::quote::__private::push_ident(&mut _s, "_check_field_compatibility");
        _s
    })parse_quote!(_check_field_compatibility),
69        )?),
70        Some(CheckForBackend::Disabled(_lit)) => None,
71        None => default_check
72            .map(|c| {
73                c(
74                    &model,
75                    &original_impl_generics,
76                    original_where_clause,
77                    &field_select_expression_type_builders,
78                )
79            })
80            .transpose()?,
81    };
82
83    let errors: TokenStream = compile_errors
84        .into_iter()
85        .map(|e| e.into_compile_error())
86        .collect();
87
88    Ok(wrap_in_dummy_mod({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "use");
    ::quote::__private::push_ident(&mut _s, "diesel");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "expression");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "Selectable");
    ::quote::__private::push_semi(&mut _s);
    ::quote::__private::push_ident(&mut _s, "impl");
    ::quote::ToTokens::to_tokens(&impl_generics, &mut _s);
    ::quote::__private::push_ident(&mut _s, "Selectable");
    ::quote::__private::push_lt(&mut _s);
    ::quote::__private::push_ident(&mut _s, "__DB");
    ::quote::__private::push_gt(&mut _s);
    ::quote::__private::push_ident(&mut _s, "for");
    ::quote::ToTokens::to_tokens(&struct_name, &mut _s);
    ::quote::ToTokens::to_tokens(&ty_generics, &mut _s);
    ::quote::ToTokens::to_tokens(&where_clause, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "type");
            ::quote::__private::push_ident(&mut _s, "SelectExpression");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    {
                        use ::quote::__private::ext::*;
                        let has_iter = ::quote::__private::HasIterator::<false>;
                        #[allow(unused_mut)]
                        let (mut field_select_expression_types, i) =
                            field_select_expression_types.quote_into_iter();
                        let has_iter = has_iter | i;
                        <_ as
                                ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                        while true {
                            let field_select_expression_types =
                                match field_select_expression_types.next() {
                                    Some(_x) => ::quote::__private::RepInterp(_x),
                                    None => break,
                                };
                            ::quote::ToTokens::to_tokens(&field_select_expression_types,
                                &mut _s);
                            ::quote::__private::push_comma(&mut _s);
                        }
                    }
                    _s
                });
            ::quote::__private::push_semi(&mut _s);
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "construct_selection");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                ::quote::__private::TokenStream::new());
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Self");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "SelectExpression");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            {
                                use ::quote::__private::ext::*;
                                let has_iter = ::quote::__private::HasIterator::<false>;
                                #[allow(unused_mut)]
                                let (mut field_select_expressions, i) =
                                    field_select_expressions.quote_into_iter();
                                let has_iter = has_iter | i;
                                <_ as
                                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                                while true {
                                    let field_select_expressions =
                                        match field_select_expressions.next() {
                                            Some(_x) => ::quote::__private::RepInterp(_x),
                                            None => break,
                                        };
                                    ::quote::ToTokens::to_tokens(&field_select_expressions,
                                        &mut _s);
                                    ::quote::__private::push_comma(&mut _s);
                                }
                            }
                            _s
                        });
                    _s
                });
            _s
        });
    ::quote::ToTokens::to_tokens(&check_function, &mut _s);
    ::quote::ToTokens::to_tokens(&errors, &mut _s);
    _s
}quote! {
89        use diesel::expression::Selectable;
90
91        impl #impl_generics Selectable<__DB>
92            for #struct_name #ty_generics
93        #where_clause
94        {
95            type SelectExpression = (#(#field_select_expression_types,)*);
96
97            fn construct_selection() -> Self::SelectExpression {
98                (#(#field_select_expressions,)*)
99            }
100        }
101
102        #check_function
103
104        #errors
105    }))
106}
107
108pub fn generate_check_function(
109    model: &Model,
110    original_impl_generics: &syn::ImplGenerics<'_>,
111    original_where_clause: Option<&syn::WhereClause>,
112    field_select_expression_type_builders: &[FieldSelectExpressionTyBuilder<'_>],
113    backends: &syn::punctuated::Punctuated<syn::TypePath, syn::token::Comma>,
114    function_name: Ident,
115) -> Result<TokenStream> {
116    let field_check_bound = model
117        .fields()
118        .iter()
119        .zip(field_select_expression_type_builders)
120        .flat_map(|(f, ty_builder)| {
121            backends.iter().map(move |b| {
122                let span = Span::mixed_site().located_at(f.ty.span());
123                let field_ty = to_field_ty_bound(f.ty_for_deserialize())?;
124                let ty = ty_builder.type_with_backend(b);
125                Ok(::syn::__private::parse_quote({
        let _span: ::quote::__private::Span =
            ::quote::__private::get_span(span).__into_span();
        let mut _s = ::quote::__private::TokenStream::new();
        ::quote::ToTokens::to_tokens(&field_ty, &mut _s);
        ::quote::__private::push_colon_spanned(&mut _s, _span);
        ::quote::__private::push_ident_spanned(&mut _s, _span, "diesel");
        ::quote::__private::push_colon2_spanned(&mut _s, _span);
        ::quote::__private::push_ident_spanned(&mut _s, _span, "deserialize");
        ::quote::__private::push_colon2_spanned(&mut _s, _span);
        ::quote::__private::push_ident_spanned(&mut _s, _span, "FromSqlRow");
        ::quote::__private::push_lt_spanned(&mut _s, _span);
        ::quote::__private::push_ident_spanned(&mut _s, _span, "diesel");
        ::quote::__private::push_colon2_spanned(&mut _s, _span);
        ::quote::__private::push_ident_spanned(&mut _s, _span, "dsl");
        ::quote::__private::push_colon2_spanned(&mut _s, _span);
        ::quote::__private::push_ident_spanned(&mut _s, _span, "SqlTypeOf");
        ::quote::__private::push_lt_spanned(&mut _s, _span);
        ::quote::ToTokens::to_tokens(&ty, &mut _s);
        ::quote::__private::push_gt_spanned(&mut _s, _span);
        ::quote::__private::push_comma_spanned(&mut _s, _span);
        ::quote::ToTokens::to_tokens(&b, &mut _s);
        ::quote::__private::push_gt_spanned(&mut _s, _span);
        _s
    })syn::parse_quote_spanned! {span =>
126                    #field_ty: diesel::deserialize::FromSqlRow<diesel::dsl::SqlTypeOf<#ty>, #b>
127                })
128            })
129        })
130        .collect::<Result<Vec<_>>>()?;
131    let where_clause = &mut original_where_clause.cloned();
132    let where_clause = where_clause.get_or_insert_with(|| ::syn::__private::parse_quote({
        let mut _s = ::quote::__private::TokenStream::new();
        ::quote::__private::push_ident(&mut _s, "where");
        _s
    })parse_quote!(where));
133    for field_check in field_check_bound {
134        where_clause.predicates.push(field_check);
135    }
136    Ok({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "fn");
    ::quote::ToTokens::to_tokens(&function_name, &mut _s);
    ::quote::ToTokens::to_tokens(&original_impl_generics, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        ::quote::__private::TokenStream::new());
    ::quote::ToTokens::to_tokens(&where_clause, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        ::quote::__private::TokenStream::new());
    _s
}quote::quote! {
137        fn #function_name #original_impl_generics()
138            #where_clause
139        {}
140    })
141}
142
143fn to_field_ty_bound(field_ty: &syn::Type) -> Result<TokenStream> {
144    match field_ty {
145        syn::Type::Reference(r) => {
146            use crate::quote::ToTokens;
147            // references are not supported for checking for now
148            //
149            // (How ever you can even have references in a `Queryable` struct anyway)
150            Err(syn::Error::new(
151                field_ty.span(),
152                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("references are not supported in `Queryable` types\nconsider using `std::borrow::Cow<\'{0}, {1}>` instead",
                r.lifetime.as_ref().expect("It's a struct field so it must have a named lifetime").ident,
                r.elem.to_token_stream()))
    })format!(
153                    "references are not supported in `Queryable` types\n\
154                         consider using `std::borrow::Cow<'{}, {}>` instead",
155                    r.lifetime
156                        .as_ref()
157                        .expect("It's a struct field so it must have a named lifetime")
158                        .ident,
159                    r.elem.to_token_stream()
160                ),
161            ))
162        }
163        field_ty => Ok({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&field_ty, &mut _s);
    _s
}quote::quote! {
164            #field_ty
165        }),
166    }
167}
168
169fn field_select_expression_ty_builder<'a>(
170    field: &'a Field,
171    model: &Model,
172    compile_errors: &mut Vec<syn::Error>,
173) -> Result<FieldSelectExpressionTyBuilder<'a>> {
174    if let Some(ref select_expression) = field.select_expression {
175        use dsl_auto_type::auto_type::expression_type_inference as type_inference;
176        let expr = &select_expression.item;
177        let (inferred_type, errors) = type_inference::infer_expression_type(
178            expr,
179            field.select_expression_type.as_ref().map(|t| &t.item),
180            &type_inference::InferrerSettings::builder()
181                .dsl_path(::syn::__private::parse_quote({
        let mut _s = ::quote::__private::TokenStream::new();
        ::quote::__private::push_ident(&mut _s, "diesel");
        ::quote::__private::push_colon2(&mut _s);
        ::quote::__private::push_ident(&mut _s, "dsl");
        _s
    })parse_quote!(diesel::dsl))
182                .function_types_case(crate::AUTO_TYPE_DEFAULT_FUNCTION_TYPE_CASE)
183                .method_types_case(crate::AUTO_TYPE_DEFAULT_METHOD_TYPE_CASE)
184                .build(),
185        );
186        compile_errors.extend(errors);
187        Ok(FieldSelectExpressionTyBuilder::Always(
188            {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&inferred_type, &mut _s);
    _s
}quote::quote!(#inferred_type),
189        ))
190    } else if let Some(ref select_expression_type) = field.select_expression_type {
191        let ty = &select_expression_type.item;
192        Ok(FieldSelectExpressionTyBuilder::Always({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&ty, &mut _s);
    _s
}quote!(#ty)))
193    } else if field.embed() {
194        Ok(FieldSelectExpressionTyBuilder::EmbedSelectable {
195            embed_ty: &field.ty,
196        })
197    } else {
198        let table_name = &model.table_names()[0];
199        let column_name = field.column_name()?.to_ident()?;
200        let span = Span::call_site();
201        Ok(FieldSelectExpressionTyBuilder::Always(
202            {
    let _span: ::quote::__private::Span =
        ::quote::__private::get_span(span).__into_span();
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&table_name, &mut _s);
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::ToTokens::to_tokens(&column_name, &mut _s);
    _s
}quote_spanned!(span=> #table_name::#column_name),
203        ))
204    }
205}
206
207pub enum FieldSelectExpressionTyBuilder<'a> {
208    Always(TokenStream),
209    EmbedSelectable { embed_ty: &'a syn::Type },
210}
211
212impl FieldSelectExpressionTyBuilder<'_> {
213    fn type_with_backend(&self, backend: &syn::TypePath) -> Cow<'_, TokenStream> {
214        match self {
215            FieldSelectExpressionTyBuilder::Always(ty) => Cow::Borrowed(ty),
216            FieldSelectExpressionTyBuilder::EmbedSelectable { embed_ty } => {
217                Cow::Owned({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_lt(&mut _s);
    ::quote::ToTokens::to_tokens(&embed_ty, &mut _s);
    ::quote::__private::push_ident(&mut _s, "as");
    ::quote::__private::push_ident(&mut _s, "Selectable");
    ::quote::__private::push_lt(&mut _s);
    ::quote::ToTokens::to_tokens(&backend, &mut _s);
    ::quote::__private::push_shr(&mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "SelectExpression");
    _s
}quote!(<#embed_ty as Selectable<#backend>>::SelectExpression))
218            }
219        }
220    }
221}
222
223fn field_column_inst(field: &Field, model: &Model) -> Result<TokenStream> {
224    if let Some(ref select_expression) = field.select_expression {
225        let expr = &select_expression.item;
226        Ok({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&expr, &mut _s);
    _s
}quote!(#expr))
227    } else if field.embed() {
228        let embed_ty = &field.ty;
229        Ok({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_lt(&mut _s);
    ::quote::ToTokens::to_tokens(&embed_ty, &mut _s);
    ::quote::__private::push_ident(&mut _s, "as");
    ::quote::__private::push_ident(&mut _s, "Selectable");
    ::quote::__private::push_lt(&mut _s);
    ::quote::__private::push_ident(&mut _s, "__DB");
    ::quote::__private::push_shr(&mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "construct_selection");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        ::quote::__private::TokenStream::new());
    _s
}quote!(<#embed_ty as Selectable<__DB>>::construct_selection()))
230    } else {
231        let table_name = &model.table_names()[0];
232        let column_name = field.column_name()?.to_ident()?;
233        let span = Span::call_site();
234        Ok({
    let _span: ::quote::__private::Span =
        ::quote::__private::get_span(span).__into_span();
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&table_name, &mut _s);
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::ToTokens::to_tokens(&column_name, &mut _s);
    _s
}quote_spanned!(span=> #table_name::#column_name))
235    }
236}