1use proc_macro::TokenStream;
3use quote::quote;
4use syn::{self, parse_macro_input, DeriveInput};
5
6#[derive(deluxe::ExtractAttributes)]
7#[deluxe(attributes(diesel_derive_pg))]
8struct Opts {
9 sql_type: syn::Type,
10}
11
12#[derive(deluxe::ExtractAttributes)]
13#[deluxe(attributes(diesel_derive_pg))]
14struct FieldAttrs {
15 sql_type: syn::Type,
16}
17
18#[proc_macro_derive(PgCustomType, attributes(diesel_derive_pg))]
47pub fn derive_sql_fn(input: TokenStream) -> TokenStream {
48 let mut ast = parse_macro_input!(input as DeriveInput);
49 let opts: Opts = deluxe::extract_attributes(&mut ast).expect("Wrong options");
50
51 let ident = &ast.ident;
52 let sql_type = opts.sql_type;
53
54 let (to_sql_impl, from_sql_impl) = match &ast.data {
55 syn::Data::Struct(data_struct) => match &data_struct.fields {
56 syn::Fields::Named(fields_named) => impl_struct(fields_named),
57 syn::Fields::Unnamed(fields_unnamed) => {
58 if fields_unnamed.unnamed.len() == 1 {
59 impl_newtype(&fields_unnamed.unnamed[0])
60 } else {
61 unimplemented!("Tuples are not implemented yet")
62 }
63 }
64 syn::Fields::Unit => unimplemented!("Units are unsupported"),
65 },
66 syn::Data::Enum(_data_enum) => unimplemented!("Enums are not implemented yet"),
67 syn::Data::Union(_data_union) => unimplemented!("Unions are unsupported"),
68 };
69
70 let (impl_generics, ty_generics, where_clause) = &ast.generics.split_for_impl();
71
72 let expanded = quote! {
73 impl #impl_generics diesel::deserialize::FromSql<#sql_type, diesel::pg::Pg> for #ident #ty_generics #where_clause {
74 fn from_sql(bytes: diesel::pg::PgValue) -> diesel::deserialize::Result<Self> {
75 #from_sql_impl
76 }
77 }
78
79 impl #impl_generics diesel::serialize::ToSql<#sql_type, diesel::pg::Pg> for #ident #ty_generics #where_clause {
80 fn to_sql<'b>(
81 &'b self,
82 out: &mut diesel::serialize::Output<'b, '_, diesel::pg::Pg>,
83 ) -> diesel::serialize::Result {
84 #to_sql_impl
85 }
86 }
87 };
88
89 TokenStream::from(expanded)
90}
91
92fn impl_newtype(field: &syn::Field) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
93 let inner_type = &field.ty;
94
95 let attrs: FieldAttrs =
96 deluxe::extract_attributes(&mut field.clone()).expect("Wrong field attributes");
97 let sql_inner_type = attrs.sql_type;
98
99 let to_sql_impl = quote! {
100 <#inner_type as diesel::serialize::ToSql<#sql_inner_type, diesel::pg::Pg>>::to_sql(
101 &self.0,
102 &mut out.reborrow(),
103 )
104
105 };
106
107 let from_sql_impl = quote! {
108 let inner =
109 diesel::deserialize::FromSql::<#sql_inner_type, diesel::pg::Pg>::from_sql(
110 bytes,
111 )?;
112 Ok(Self(inner))
113 };
114
115 (to_sql_impl, from_sql_impl)
116}
117
118fn impl_struct(
119 fields_named: &syn::FieldsNamed,
120 ) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
122 let named = &fields_named.named;
123
124 let sql_inner_types: Vec<_> = named
125 .clone()
126 .iter_mut()
127 .map(|field| {
128 let attrs: FieldAttrs =
129 deluxe::extract_attributes(field).expect("Wrong field attributes");
130 attrs.sql_type
131 })
132 .collect();
133
134 let cloned_fields = named
135 .iter()
136 .map(|field| &field.ident)
137 .map(|field_ident| quote! { self.#field_ident.clone() });
138
139 let to_sql_impl = quote! {
140 diesel::serialize::WriteTuple::<(
141 #(#sql_inner_types),*
142 )>::write_tuple(
143 &(#(#cloned_fields),*),
144 &mut out.reborrow(),
145 )
146 };
147
148 let field_idents = named.iter().map(|field| &field.ident);
149 let field_idents2 = field_idents.clone();
150
151 let from_sql_impl = quote! {
152 let (#(#field_idents),*) =
153 diesel::deserialize::FromSql::<diesel::sql_types::Record<(#(#sql_inner_types),*)>, diesel::pg::Pg>::from_sql(bytes)?;
154 Ok(Self {
155 #(#field_idents2),*
156 })
157 };
158
159 (to_sql_impl, from_sql_impl)
160}