pgrx_sql_entity_graph/postgres_type/
mod.rs1pub mod entity;
19
20use crate::enrich::{ToEntityGraphTokens, ToRustCodeTokens};
21use proc_macro2::{Ident, TokenStream as TokenStream2};
22use quote::{format_ident, quote};
23use syn::parse::{Parse, ParseStream};
24use syn::{DeriveInput, Generics, ItemStruct, Lifetime, LifetimeParam};
25
26pub use crate::postgres_type::entity::Alignment;
27use crate::{CodeEnrichment, ToSqlConfig};
28
29#[derive(Debug, Clone)]
53pub struct PostgresTypeDerive {
54 name: Ident,
55 generics: Generics,
56 in_fn: Ident,
57 out_fn: Ident,
58 receive_fn: Option<Ident>,
59 send_fn: Option<Ident>,
60 to_sql_config: ToSqlConfig,
61 alignment: Alignment,
62}
63
64impl PostgresTypeDerive {
65 pub fn new(
66 name: Ident,
67 generics: Generics,
68 in_fn: Ident,
69 out_fn: Ident,
70 receive_fn: Option<Ident>,
71 send_fn: Option<Ident>,
72 to_sql_config: ToSqlConfig,
73 alignment: Alignment,
74 ) -> Result<CodeEnrichment<Self>, syn::Error> {
75 if !to_sql_config.overrides_default() {
76 crate::ident_is_acceptable_to_postgres(&name)?;
77 }
78 Ok(CodeEnrichment(Self {
79 generics,
80 name,
81 in_fn,
82 out_fn,
83 receive_fn,
84 send_fn,
85 to_sql_config,
86 alignment,
87 }))
88 }
89
90 pub fn from_derive_input(
91 derive_input: DeriveInput,
92 pg_binary_protocol: bool,
93 ) -> Result<CodeEnrichment<Self>, syn::Error> {
94 match derive_input.data {
95 syn::Data::Struct(_) | syn::Data::Enum(_) => {}
96 syn::Data::Union(_) => {
97 return Err(syn::Error::new(derive_input.ident.span(), "expected struct or enum"));
98 }
99 };
100 let to_sql_config =
101 ToSqlConfig::from_attributes(derive_input.attrs.as_slice())?.unwrap_or_default();
102 let funcname_in = Ident::new(
103 &format!("{}_in", derive_input.ident).to_lowercase(),
104 derive_input.ident.span(),
105 );
106 let funcname_out = Ident::new(
107 &format!("{}_out", derive_input.ident).to_lowercase(),
108 derive_input.ident.span(),
109 );
110 let funcname_receive = (pg_binary_protocol).then(|| {
111 Ident::new(
112 &format!("{}_recv", derive_input.ident).to_lowercase(),
113 derive_input.ident.span(),
114 )
115 });
116 let funcname_send = (pg_binary_protocol).then(|| {
117 Ident::new(
118 &format!("{}_send", derive_input.ident).to_lowercase(),
119 derive_input.ident.span(),
120 )
121 });
122 let alignment = Alignment::from_attributes(derive_input.attrs.as_slice())?;
123 Self::new(
124 derive_input.ident,
125 derive_input.generics,
126 funcname_in,
127 funcname_out,
128 funcname_receive,
129 funcname_send,
130 to_sql_config,
131 alignment,
132 )
133 }
134}
135
136impl ToEntityGraphTokens for PostgresTypeDerive {
137 fn to_entity_graph_tokens(&self) -> TokenStream2 {
138 let name = &self.name;
139 let generics = self.generics.clone();
140 let (impl_generics, ty_generics, where_clauses) = generics.split_for_impl();
141
142 let mut anon_generics = generics.clone();
144 anon_generics.params = anon_generics
145 .params
146 .into_iter()
147 .flat_map(|param| match param {
148 item @ syn::GenericParam::Type(_) | item @ syn::GenericParam::Const(_) => {
149 Some(item)
150 }
151 syn::GenericParam::Lifetime(lt_def) => Some(syn::GenericParam::Lifetime(
152 LifetimeParam::new(Lifetime::new("'_", lt_def.lifetime.span())),
153 )),
154 })
155 .collect();
156 let (_, anon_ty_gen, _) = anon_generics.split_for_impl();
157
158 let in_fn = &self.in_fn;
159 let out_fn = &self.out_fn;
160 let stringify_receive_fn = self
161 .receive_fn
162 .as_ref()
163 .map(|f| quote! { Some(stringify!(#f)) })
164 .unwrap_or_else(|| quote! { None });
165 let stringify_send_fn = self
166 .send_fn
167 .as_ref()
168 .map(|f| quote! { Some(stringify!(#f)) })
169 .unwrap_or_else(|| quote! { None });
170 let receive_fn_module_path = self
171 .receive_fn
172 .as_ref()
173 .map(|f| {
174 quote! {Some({
175 let in_fn = stringify!(#f);
176 let mut path_items: Vec<_> = in_fn.split("::").collect();
177 let _ = path_items.pop(); path_items.join("::")
179 })}
180 })
181 .unwrap_or_else(|| quote! { None });
182 let send_fn_module_path = self
183 .send_fn
184 .as_ref()
185 .map(|f| {
186 quote! {Some({
187 let out_fn = stringify!(#f);
188 let mut path_items: Vec<_> = out_fn.split("::").collect();
189 let _ = path_items.pop(); path_items.join("::")
191 })}
192 })
193 .unwrap_or_else(|| quote! { None });
194
195 let sql_graph_entity_fn_name = format_ident!("__pgrx_internals_type_{}", self.name);
196
197 let to_sql_config = &self.to_sql_config;
198
199 let alignment = match &self.alignment {
200 Alignment::On => quote! { Some(::std::mem::align_of::<#name>()) },
201 Alignment::Off => quote! { None },
202 };
203
204 quote! {
205 unsafe impl #impl_generics ::pgrx::pgrx_sql_entity_graph::metadata::SqlTranslatable for #name #ty_generics #where_clauses {
206 fn argument_sql() -> core::result::Result<::pgrx::pgrx_sql_entity_graph::metadata::SqlMapping, ::pgrx::pgrx_sql_entity_graph::metadata::ArgumentError> {
207 Ok(::pgrx::pgrx_sql_entity_graph::metadata::SqlMapping::As(String::from(stringify!(#name))))
208 }
209
210 fn return_sql() -> core::result::Result<::pgrx::pgrx_sql_entity_graph::metadata::Returns, ::pgrx::pgrx_sql_entity_graph::metadata::ReturnsError> {
211 Ok(::pgrx::pgrx_sql_entity_graph::metadata::Returns::One(::pgrx::pgrx_sql_entity_graph::metadata::SqlMapping::As(String::from(stringify!(#name)))))
212 }
213 }
214
215
216 #[unsafe(no_mangle)]
217 #[doc(hidden)]
218 #[allow(nonstandard_style, unknown_lints, clippy::no_mangle_with_rust_abi)]
219 pub extern "Rust" fn #sql_graph_entity_fn_name() -> ::pgrx::pgrx_sql_entity_graph::SqlGraphEntity {
220 extern crate alloc;
221 use alloc::vec::Vec;
222 use alloc::vec;
223 use alloc::string::{String, ToString};
224 use ::pgrx::datum::WithTypeIds;
225
226 let mut mappings = Default::default();
227 <#name #anon_ty_gen as ::pgrx::datum::WithTypeIds>::register_with_refs(
228 &mut mappings,
229 stringify!(#name).to_string()
230 );
231 ::pgrx::datum::WithSizedTypeIds::<#name #anon_ty_gen>::register_sized_with_refs(
232 &mut mappings,
233 stringify!(#name).to_string()
234 );
235 ::pgrx::datum::WithArrayTypeIds::<#name #anon_ty_gen>::register_array_with_refs(
236 &mut mappings,
237 stringify!(#name).to_string()
238 );
239 ::pgrx::datum::WithVarlenaTypeIds::<#name #anon_ty_gen>::register_varlena_with_refs(
240 &mut mappings,
241 stringify!(#name).to_string()
242 );
243 let submission = ::pgrx::pgrx_sql_entity_graph::PostgresTypeEntity {
244 name: stringify!(#name),
245 file: file!(),
246 line: line!(),
247 module_path: module_path!(),
248 full_path: core::any::type_name::<#name #anon_ty_gen>(),
249 mappings: mappings.into_iter().collect(),
250 in_fn: stringify!(#in_fn),
251 in_fn_module_path: {
252 let in_fn = stringify!(#in_fn);
253 let mut path_items: Vec<_> = in_fn.split("::").collect();
254 let _ = path_items.pop(); path_items.join("::")
256 },
257 out_fn: stringify!(#out_fn),
258 out_fn_module_path: {
259 let out_fn = stringify!(#out_fn);
260 let mut path_items: Vec<_> = out_fn.split("::").collect();
261 let _ = path_items.pop(); path_items.join("::")
263 },
264 receive_fn: #stringify_receive_fn,
265 receive_fn_module_path: #receive_fn_module_path,
266 send_fn: #stringify_send_fn,
267 send_fn_module_path: #send_fn_module_path,
268 to_sql_config: #to_sql_config,
269 alignment: #alignment,
270 };
271 ::pgrx::pgrx_sql_entity_graph::SqlGraphEntity::Type(submission)
272 }
273 }
274 }
275}
276
277impl ToRustCodeTokens for PostgresTypeDerive {}
278
279impl Parse for CodeEnrichment<PostgresTypeDerive> {
280 fn parse(input: ParseStream) -> Result<Self, syn::Error> {
281 let ItemStruct { attrs, ident, generics, .. } = input.parse()?;
282
283 let pg_binary_protocol = attrs.iter().any(|a| a.path().is_ident("pg_binary_protocol"));
284
285 let to_sql_config = ToSqlConfig::from_attributes(attrs.as_slice())?.unwrap_or_default();
286 let in_fn = Ident::new(&format!("{ident}_in").to_lowercase(), ident.span());
287 let out_fn = Ident::new(&format!("{ident}_out").to_lowercase(), ident.span());
288 let receive_fn = (pg_binary_protocol)
289 .then(|| Ident::new(&format!("{ident}_recv").to_lowercase(), ident.span()));
290 let send_fn = (pg_binary_protocol)
291 .then(|| Ident::new(&format!("{ident}_send").to_lowercase(), ident.span()));
292 let alignment = Alignment::from_attributes(attrs.as_slice())?;
293 PostgresTypeDerive::new(
294 ident,
295 generics,
296 in_fn,
297 out_fn,
298 receive_fn,
299 send_fn,
300 to_sql_config,
301 alignment,
302 )
303 }
304}