diesel_derives/
sql_type.rs
1use proc_macro2::{Span, TokenStream};
2use quote::quote;
3use syn::Result;
4use syn::{DeriveInput, Ident};
5
6use crate::model::Model;
7use crate::parsers::PostgresType;
8use crate::util::wrap_in_dummy_mod;
9
10pub fn derive(item: DeriveInput) -> Result<TokenStream> {
11 let model = Model::from_item(&item, true, false)?;
12
13 let struct_name = &item.ident;
14 let generic_count = item.generics.params.len();
15 let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl();
16
17 let sqlite_tokens = sqlite_tokens(&item, &model);
18 let mysql_tokens = mysql_tokens(&item, &model);
19 let pg_tokens = pg_tokens(&item, &model);
20
21 let is_array = struct_name == "Array" && generic_count == 1;
22
23 Ok(wrap_in_dummy_mod(quote! {
24 impl #impl_generics diesel::sql_types::SqlType
25 for #struct_name #ty_generics
26 #where_clause
27 {
28 type IsNull = diesel::sql_types::is_nullable::NotNull;
29
30 const IS_ARRAY: bool = #is_array;
31 }
32
33 impl #impl_generics diesel::sql_types::SingleValue
34 for #struct_name #ty_generics
35 #where_clause
36 {
37 }
38
39 #sqlite_tokens
40 #mysql_tokens
41 #pg_tokens
42 }))
43}
44
45fn sqlite_tokens(item: &DeriveInput, model: &Model) -> Option<TokenStream> {
46 model
47 .sqlite_type
48 .as_ref()
49 .map(|sqlite_type| Ident::new(&sqlite_type.name.value(), Span::call_site()))
50 .and_then(|ty| {
51 if cfg!(not(feature = "sqlite")) {
52 return None;
53 }
54
55 let struct_name = &item.ident;
56 let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl();
57
58 Some(quote! {
59 impl #impl_generics diesel::sql_types::HasSqlType<#struct_name #ty_generics>
60 for diesel::sqlite::Sqlite
61 #where_clause
62 {
63 fn metadata(_: &mut ()) -> diesel::sqlite::SqliteType {
64 diesel::sqlite::SqliteType::#ty
65 }
66 }
67 })
68 })
69}
70
71fn mysql_tokens(item: &DeriveInput, model: &Model) -> Option<TokenStream> {
72 model
73 .mysql_type
74 .as_ref()
75 .map(|mysql_type| Ident::new(&mysql_type.name.value(), Span::call_site()))
76 .and_then(|ty| {
77 if cfg!(not(feature = "mysql")) {
78 return None;
79 }
80
81 let struct_name = &item.ident;
82 let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl();
83
84 Some(quote! {
85 impl #impl_generics diesel::sql_types::HasSqlType<#struct_name #ty_generics>
86 for diesel::mysql::Mysql
87 #where_clause
88 {
89 fn metadata(_: &mut ()) -> diesel::mysql::MysqlType {
90 diesel::mysql::MysqlType::#ty
91 }
92 }
93 })
94 })
95}
96
97fn pg_tokens(item: &DeriveInput, model: &Model) -> Option<TokenStream> {
98 model.postgres_type.as_ref().and_then(|ty| {
99 if cfg!(not(feature = "postgres")) {
100 return None;
101 }
102
103 let struct_name = &item.ident;
104 let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl();
105
106 let metadata_fn = match ty {
107 PostgresType::Fixed(oid, array_oid) => quote!(
108 fn metadata(_: &mut Self::MetadataLookup) -> PgTypeMetadata {
109 PgTypeMetadata::new(#oid, #array_oid)
110 }
111 ),
112 PostgresType::Lookup(type_name, Some(type_schema)) => quote!(
113 fn metadata(lookup: &mut Self::MetadataLookup) -> PgTypeMetadata {
114 lookup.lookup_type(#type_name, Some(#type_schema))
115 }
116 ),
117 PostgresType::Lookup(type_name, None) => quote!(
118 fn metadata(lookup: &mut Self::MetadataLookup) -> PgTypeMetadata {
119 lookup.lookup_type(#type_name, None)
120 }
121 ),
122 };
123
124 Some(quote! {
125 use diesel::pg::{PgMetadataLookup, PgTypeMetadata};
126
127 impl #impl_generics diesel::sql_types::HasSqlType<#struct_name #ty_generics>
128 for diesel::pg::Pg
129 #where_clause
130 {
131 #metadata_fn
132 }
133 })
134 })
135}