1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
extern crate proc_macro;
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote, ToTokens, TokenStreamExt};
use syn::{parse_macro_input, punctuated::Punctuated, ItemStruct, Lit, MetaNameValue, Token};
#[proc_macro_attribute]
pub fn wrappers_fdw(attr: TokenStream, item: TokenStream) -> TokenStream {
let mut metas = TokenStream2::new();
let meta_attrs: Punctuated<MetaNameValue, Token![,]> =
parse_macro_input!(attr with Punctuated::parse_terminated);
for attr in meta_attrs {
let name = format!("{}", attr.path.segments.first().unwrap().ident);
if let Lit::Str(val) = attr.lit {
let value = val.value();
metas.append_all(quote! {
meta.insert(#name.to_owned(), #value.to_owned());
});
}
}
let item: ItemStruct = parse_macro_input!(item as ItemStruct);
let item_tokens = item.to_token_stream();
let ident = item.ident;
let ident_str = ident.to_string();
let ident_snake = to_snake_case(ident_str.as_str());
let module_ident = format_ident!("__{}_pgx", ident_snake);
let fn_ident = format_ident!("{}_handler", ident_snake);
let fn_validator_ident = format_ident!("{}_validator", ident_snake);
let fn_meta_ident = format_ident!("{}_meta", ident_snake);
let quoted = quote! {
#item_tokens
mod #module_ident {
use super::#ident;
use std::collections::HashMap;
use pgx::prelude::*;
use supabase_wrappers::prelude::*;
#[pg_extern(create_or_replace)]
fn #fn_ident() -> supabase_wrappers::FdwRoutine {
#ident::fdw_routine()
}
#[pg_extern(create_or_replace)]
fn #fn_validator_ident(options: Vec<Option<String>>, catalog: Option<pg_sys::Oid>) {
#ident::validator(options, catalog)
}
#[pg_extern(create_or_replace)]
fn #fn_meta_ident() -> TableIterator<'static, (
name!(name, Option<String>),
name!(version, Option<String>),
name!(author, Option<String>),
name!(website, Option<String>)
)> {
let mut meta: HashMap<String, String> = HashMap::new();
#metas
TableIterator::new(vec![(
Some(#ident_str.to_owned()),
meta.get("version").map(|s| s.to_owned()),
meta.get("author").map(|s| s.to_owned()),
meta.get("website").map(|s| s.to_owned()),
)].into_iter())
}
}
};
quoted.into()
}
fn to_snake_case(s: &str) -> String {
let mut acc = String::new();
let mut prev = '_';
for ch in s.chars() {
if ch.is_uppercase() && prev != '_' {
acc.push('_');
}
acc.push(ch);
prev = ch;
}
acc.to_lowercase()
}