1#![recursion_limit = "128"]
9
10extern crate proc_macro;
11extern crate proc_macro2;
12#[macro_use]
13extern crate syn;
14#[macro_use]
15extern crate quote;
16
17use proc_macro2::{Ident, Span, TokenStream};
18use quote::ToTokens;
19use syn::punctuated::Punctuated;
20use syn::token::Comma;
21
22fn create_function_params(num_args: usize) -> TokenStream {
23 let mut tokens = TokenStream::new();
24
25 for i in 0..num_args {
26 let arg_name = Ident::new(&format!("arg_{}", i), Span::call_site());
27
28 tokens.extend(quote!(
29 #arg_name,
30 ));
31 }
32
33 tokens
34}
35
36fn extract_arg_data(inputs: &Punctuated<syn::FnArg, Comma>) -> TokenStream {
37 let mut get_args_stream = TokenStream::new();
38
39 for (i, arg) in inputs.iter().enumerate() {
40 let arg_type: &syn::Type = match *arg {
41 syn::FnArg::SelfRef(_) | syn::FnArg::SelfValue(_) => {
42 panic!("self functions not supported")
43 }
44 syn::FnArg::Inferred(_) => panic!("inferred function parameters not supported"),
45 syn::FnArg::Captured(ref captured) => &captured.ty,
46 syn::FnArg::Ignored(ref ty) => ty,
47 };
48
49 let arg_name = Ident::new(&format!("arg_{}", i), Span::call_site());
50 let arg_error = format!("unsupported function argument type for {}", arg_name);
51
52 let get_arg = quote!(
53 let #arg_name: #arg_type = pg_extend::pg_datum::TryFromPgDatum::try_from(
54 pg_extend::pg_datum::PgDatum::from_raw(
55 args[#i],
56 args_null[#i]
57 ),
58 )
59 .expect(#arg_error);
60 );
61
62 get_args_stream.extend(get_arg);
63 }
64
65 get_args_stream
66}
67
68fn impl_info_for_fn(item: &syn::Item) -> TokenStream {
69 let func = if let syn::Item::Fn(func) = item {
70 func
71 } else {
72 panic!("annotation only supported on functions");
73 };
74
75 let func_name = &func.ident;
76 let func_decl = &func.decl;
77
78 if func_decl.variadic.is_some() {
79 panic!("variadic functions (...) not supported")
80 }
81
82 let inputs = &func_decl.inputs;
84 let mut function = item.clone().into_token_stream();
89
90 let func_wrapper_name = syn::Ident::new(&format!("pg_{}", func_name), Span::call_site());
91 let func_info_name = syn::Ident::new(
92 &format!("pg_finfo_{}", func_wrapper_name),
93 Span::call_site(),
94 );
95
96 let func_info = quote!(
98 #[no_mangle]
99 pub extern "C" fn #func_info_name () -> &'static pg_extend::pg_sys::Pg_finfo_record {
100 const my_finfo: pg_extend::pg_sys::Pg_finfo_record = pg_extend::pg_sys::Pg_finfo_record { api_version: 1 };
101 &my_finfo
102 }
103 );
104
105 function.extend(func_info);
107
108 let get_args_from_datums = extract_arg_data(inputs);
109 let func_params = create_function_params(inputs.len());
110
111 let func_wrapper = quote!(
113 #[no_mangle]
114 pub extern "C" fn #func_wrapper_name (func_call_info: pg_extend::pg_sys::FunctionCallInfo) -> pg_extend::pg_sys::Datum {
115 use std::panic;
116
117 let func_info: &mut pg_extend::pg_sys::FunctionCallInfoData = unsafe {
118 func_call_info
119 .as_mut()
120 .expect("func_call_info was unexpectedly NULL")
121 };
122
123 let panic_result = panic::catch_unwind(|| {
125 let (args, args_null) = pg_extend::get_args(func_info);
127
128 #get_args_from_datums
131
132 let result = #func_name(#func_params);
134
135 pg_extend::pg_datum::PgDatum::from(result)
137 });
138
139 match panic_result {
141 Ok(result) => {
142 func_info.isnull = result.is_null();
144
145 result.into_datum()
147 }
148 Err(err) => {
149 func_info.isnull = true;
151
152 panic::resume_unwind(err)
154 }
155 }
156 }
157 );
158
159 function.extend(func_wrapper);
160 function
161}
162
163#[proc_macro_attribute]
207pub fn pg_extern(
208 _attr: proc_macro::TokenStream,
209 item: proc_macro::TokenStream,
210) -> proc_macro::TokenStream {
211 let ast: syn::Item = parse_macro_input!(item as syn::Item);
213
214 let expanded: TokenStream = impl_info_for_fn(&ast);
216
217 proc_macro::TokenStream::from(expanded)
219}