pgx_utils/sql_entity_graph/postgres_type/
mod.rs1pub mod entity;
18
19use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
20use quote::{quote, ToTokens, TokenStreamExt};
21use syn::parse::{Parse, ParseStream};
22use syn::{DeriveInput, Generics, ItemStruct};
23
24use crate::sql_entity_graph::ToSqlConfig;
25
26#[derive(Debug, Clone)]
49pub struct PostgresType {
50 name: Ident,
51 generics: Generics,
52 in_fn: Ident,
53 out_fn: Ident,
54 to_sql_config: ToSqlConfig,
55}
56
57impl PostgresType {
58 pub fn new(
59 name: Ident,
60 generics: Generics,
61 in_fn: Ident,
62 out_fn: Ident,
63 to_sql_config: ToSqlConfig,
64 ) -> Result<Self, syn::Error> {
65 if !to_sql_config.overrides_default() {
66 crate::ident_is_acceptable_to_postgres(&name)?;
67 }
68 Ok(Self { generics, name, in_fn, out_fn, to_sql_config })
69 }
70
71 pub fn from_derive_input(derive_input: DeriveInput) -> Result<Self, syn::Error> {
72 match derive_input.data {
73 syn::Data::Struct(_) | syn::Data::Enum(_) => {}
74 syn::Data::Union(_) => {
75 return Err(syn::Error::new(derive_input.ident.span(), "expected struct or enum"))
76 }
77 };
78 let to_sql_config =
79 ToSqlConfig::from_attributes(derive_input.attrs.as_slice())?.unwrap_or_default();
80 let funcname_in = Ident::new(
81 &format!("{}_in", derive_input.ident).to_lowercase(),
82 derive_input.ident.span(),
83 );
84 let funcname_out = Ident::new(
85 &format!("{}_out", derive_input.ident).to_lowercase(),
86 derive_input.ident.span(),
87 );
88 Self::new(
89 derive_input.ident,
90 derive_input.generics,
91 funcname_in,
92 funcname_out,
93 to_sql_config,
94 )
95 }
96}
97
98impl Parse for PostgresType {
99 fn parse(input: ParseStream) -> Result<Self, syn::Error> {
100 let parsed: ItemStruct = input.parse()?;
101 let to_sql_config =
102 ToSqlConfig::from_attributes(parsed.attrs.as_slice())?.unwrap_or_default();
103 let funcname_in =
104 Ident::new(&format!("{}_in", parsed.ident).to_lowercase(), parsed.ident.span());
105 let funcname_out =
106 Ident::new(&format!("{}_out", parsed.ident).to_lowercase(), parsed.ident.span());
107 Self::new(parsed.ident, parsed.generics, funcname_in, funcname_out, to_sql_config)
108 }
109}
110
111impl ToTokens for PostgresType {
112 fn to_tokens(&self, tokens: &mut TokenStream2) {
113 let name = &self.name;
114 let mut static_generics = self.generics.clone();
115 static_generics.params = static_generics
116 .params
117 .clone()
118 .into_iter()
119 .flat_map(|param| match param {
120 item @ syn::GenericParam::Type(_) | item @ syn::GenericParam::Const(_) => {
121 Some(item)
122 }
123 syn::GenericParam::Lifetime(mut lifetime) => {
124 lifetime.lifetime.ident = Ident::new("static", Span::call_site());
125 Some(syn::GenericParam::Lifetime(lifetime))
126 }
127 })
128 .collect();
129 let mut staticless_generics = self.generics.clone();
130 staticless_generics.params = static_generics
131 .params
132 .clone()
133 .into_iter()
134 .flat_map(|param| match param {
135 item @ syn::GenericParam::Type(_) | item @ syn::GenericParam::Const(_) => {
136 Some(item)
137 }
138 syn::GenericParam::Lifetime(_) => None,
139 })
140 .collect();
141 let (staticless_impl_generics, _staticless_ty_generics, _staticless_where_clauses) =
142 staticless_generics.split_for_impl();
143 let (_static_impl_generics, static_ty_generics, static_where_clauses) =
144 static_generics.split_for_impl();
145
146 let in_fn = &self.in_fn;
147 let out_fn = &self.out_fn;
148
149 let sql_graph_entity_fn_name =
150 syn::Ident::new(&format!("__pgx_internals_type_{}", self.name), Span::call_site());
151
152 let to_sql_config = &self.to_sql_config;
153
154 let inv = quote! {
155 unsafe impl #staticless_impl_generics ::pgx::utils::sql_entity_graph::metadata::SqlTranslatable for #name #static_ty_generics #static_where_clauses {
156 fn argument_sql() -> core::result::Result<::pgx::utils::sql_entity_graph::metadata::SqlMapping, ::pgx::utils::sql_entity_graph::metadata::ArgumentError> {
157 Ok(::pgx::utils::sql_entity_graph::metadata::SqlMapping::As(String::from(stringify!(#name))))
158 }
159
160 fn return_sql() -> core::result::Result<::pgx::utils::sql_entity_graph::metadata::Returns, ::pgx::utils::sql_entity_graph::metadata::ReturnsError> {
161 Ok(::pgx::utils::sql_entity_graph::metadata::Returns::One(::pgx::utils::sql_entity_graph::metadata::SqlMapping::As(String::from(stringify!(#name)))))
162 }
163 }
164
165
166 #[no_mangle]
167 #[doc(hidden)]
168 pub extern "Rust" fn #sql_graph_entity_fn_name() -> ::pgx::utils::sql_entity_graph::SqlGraphEntity {
169 extern crate alloc;
170 use alloc::vec::Vec;
171 use alloc::vec;
172 use alloc::string::{String, ToString};
173 use ::pgx::WithTypeIds;
174
175 let mut mappings = Default::default();
176 <#name #static_ty_generics as pgx::datum::WithTypeIds>::register_with_refs(
177 &mut mappings,
178 stringify!(#name).to_string()
179 );
180 pgx::datum::WithSizedTypeIds::<#name #static_ty_generics>::register_sized_with_refs(
181 &mut mappings,
182 stringify!(#name).to_string()
183 );
184 pgx::datum::WithArrayTypeIds::<#name #static_ty_generics>::register_array_with_refs(
185 &mut mappings,
186 stringify!(#name).to_string()
187 );
188 pgx::datum::WithVarlenaTypeIds::<#name #static_ty_generics>::register_varlena_with_refs(
189 &mut mappings,
190 stringify!(#name).to_string()
191 );
192 let submission = ::pgx::utils::sql_entity_graph::PostgresTypeEntity {
193 name: stringify!(#name),
194 file: file!(),
195 line: line!(),
196 module_path: module_path!(),
197 full_path: core::any::type_name::<#name #static_ty_generics>(),
198 mappings,
199 in_fn: stringify!(#in_fn),
200 in_fn_module_path: {
201 let in_fn = stringify!(#in_fn);
202 let mut path_items: Vec<_> = in_fn.split("::").collect();
203 let _ = path_items.pop(); path_items.join("::")
205 },
206 out_fn: stringify!(#out_fn),
207 out_fn_module_path: {
208 let out_fn = stringify!(#out_fn);
209 let mut path_items: Vec<_> = out_fn.split("::").collect();
210 let _ = path_items.pop(); path_items.join("::")
212 },
213 to_sql_config: #to_sql_config,
214 };
215 ::pgx::utils::sql_entity_graph::SqlGraphEntity::Type(submission)
216 }
217 };
218 tokens.append_all(inv);
219 }
220}