use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::Result;
use syn::{DeriveInput, Ident};
use crate::model::Model;
use crate::util::wrap_in_dummy_mod;
use diesel_attribute_parser::parsers::PostgresType;
pub fn derive(item: DeriveInput) -> Result<TokenStream> {
let model = Model::from_item(&item, true, false)?;
let struct_name = &item.ident;
let generic_count = item.generics.params.len();
let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl();
let sqlite_tokens = sqlite_tokens(&item, &model);
let mysql_tokens = mysql_tokens(&item, &model);
let mariadb_tokens = mariadb_tokens(&item, &model);
let pg_tokens = pg_tokens(&item, &model);
let is_array = struct_name == "Array" && generic_count == 1;
let enum_sql_type = if model.enum_type {
let mut generics = item.generics.clone();
generics.params.push(syn::parse_quote!(__DB));
generics
.params
.push(syn::parse_quote!(const __ANYWAY: bool));
generics
.make_where_clause()
.predicates
.push(syn::parse_quote!(__DB: diesel::backend::Backend));
generics
.make_where_clause()
.predicates
.push(syn::parse_quote!(diesel::internal::derives::sql_type::EnumTypeMapping: diesel::internal::derives::sql_type::EnumMapping<__DB>));
let (impl_generics, _, where_clause) = generics.split_for_impl();
Some(quote::quote! {
impl #impl_generics diesel::sql_types::EnumSqlType<__ANYWAY, __DB> for #struct_name #ty_generics
#where_clause
{
type Strategy = diesel::internal::derives::sql_type::EnumTypeMapping;
}
})
} else {
None
};
Ok(wrap_in_dummy_mod(quote! {
impl #impl_generics diesel::sql_types::SqlType
for #struct_name #ty_generics
#where_clause
{
type IsNull = diesel::sql_types::is_nullable::NotNull;
const IS_ARRAY: bool = #is_array;
}
impl #impl_generics diesel::sql_types::SingleValue
for #struct_name #ty_generics
#where_clause
{
}
#enum_sql_type
#sqlite_tokens
#mysql_tokens
#mariadb_tokens
#pg_tokens
}))
}
fn sqlite_tokens(item: &DeriveInput, model: &Model) -> Option<TokenStream> {
model
.sqlite_type
.as_ref()
.map(|sqlite_type| Ident::new(&sqlite_type.name.value(), Span::mixed_site()))
.map(|ty| {
let struct_name = &item.ident;
let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl();
quote! {
diesel::internal::derives::sql_type::expand_sqlite! {
impl #impl_generics diesel::sql_types::HasSqlType<#struct_name #ty_generics>
for diesel::sqlite::Sqlite
#where_clause
{
fn metadata(_: &mut ()) -> diesel::sqlite::SqliteType {
diesel::sqlite::SqliteType::#ty
}
}}
}
})
}
fn mysql_tokens(item: &DeriveInput, model: &Model) -> Option<TokenStream> {
model
.mysql_type
.as_ref()
.map(|mysql_type| Ident::new(&mysql_type.name.value(), Span::mixed_site()))
.map(|ty| {
let struct_name = &item.ident;
let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl();
quote! {
diesel::internal::derives::sql_type::expand_mysql! {
impl #impl_generics diesel::sql_types::HasSqlType<#struct_name #ty_generics>
for diesel::mysql::Mysql
#where_clause
{
fn metadata(_: &mut ()) -> diesel::mysql::MysqlType {
diesel::mysql::MysqlType::#ty
}
}
}
}
})
}
fn mariadb_tokens(item: &DeriveInput, model: &Model) -> Option<TokenStream> {
model
.mariadb_type
.as_ref()
.map(|mariadb_type| Ident::new(&mariadb_type.name.value(), Span::mixed_site()))
.map(|ty| {
let struct_name = &item.ident;
let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl();
quote! {
diesel::internal::derives::sql_type::expand_mariadb! {
impl #impl_generics diesel::sql_types::HasSqlType<#struct_name #ty_generics>
for diesel::mariadb::Mariadb
#where_clause
{
fn metadata(_: &mut ()) -> diesel::mariadb::MariadbType {
diesel::mariadb::MariadbType::#ty
}
}
}
}
})
}
fn pg_tokens(item: &DeriveInput, model: &Model) -> Option<TokenStream> {
model.postgres_type.as_ref().map(|ty| {
let struct_name = &item.ident;
let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl();
let metadata_fn = match ty {
PostgresType::Fixed(oid, array_oid) => quote!(
fn metadata(_: &mut Self::MetadataLookup) -> diesel::pg::PgTypeMetadata {
diesel::pg::PgTypeMetadata::new(#oid, #array_oid)
}
),
PostgresType::Lookup(type_name, Some(type_schema)) => quote!(
fn metadata(lookup: &mut Self::MetadataLookup) -> diesel::pg::PgTypeMetadata {
use diesel::pg::PgMetadataLookup;
lookup.lookup_type(#type_name, Some(#type_schema))
}
),
PostgresType::Lookup(type_name, None) => quote!(
fn metadata(lookup: &mut Self::MetadataLookup) -> diesel::pg::PgTypeMetadata {
use diesel::pg::PgMetadataLookup;
lookup.lookup_type(#type_name, None)
}
),
};
quote! {
diesel::internal::derives::sql_type::expand_pg! {
impl #impl_generics diesel::sql_types::HasSqlType<#struct_name #ty_generics>
for diesel::pg::Pg
#where_clause
{
#metadata_fn
}
}
}
})
}