opengauss_bindgen_macros/
lib.rs

1use proc_macro::TokenStream;
2use proc_macro2::Span;
3use quote::quote;
4use quote::ToTokens;
5use std::borrow::Borrow;
6use std::borrow::BorrowMut;
7use syn::{parse, parse_str, FnArg, ItemFn, Pat, Stmt};
8
9fn gen_opengauss_type(ty: &syn::Type) -> Box<syn::Type> {
10    match ty.to_owned().into_token_stream().to_string().as_str() {
11        "i8" | "u8" | "i16" | "u16" | "i32" | "u32" | "i64" | "u64" | "bool" | "char" => {
12            Box::new(syn::Type::from(parse_str::<syn::TypePath>("i64").unwrap()))
13        }
14        "f32" | "f64" => Box::new(syn::Type::from(parse_str::<syn::TypePath>("f64").unwrap())),
15        _ => Box::new(syn::Type::from(parse_str::<syn::TypePath>("i32").unwrap())),
16    }
17}
18
19fn gen_from_expr(ty: &syn::Type, id: &str) -> String {
20    let type_str = ty.to_owned().into_token_stream().to_string();
21    match type_str.as_str() {
22        "i8" | "u8" | "i16" | "u16" | "i32" | "u32" | "i64" | "u64" | "bool" | "char" => {
23            format!("{} as {}", id, type_str)
24        }
25        "f32" | "f64" => format!("{} as f64", id),
26        _ => format!("<{}>::from_opengauss_type({})", type_str, id),
27    }
28}
29
30fn gen_into_expr(return_type_str: &str, expr: syn::Expr) -> syn::Expr {
31    let expr_str = expr.into_token_stream().to_string();
32    parse_str::<syn::Expr>(&match return_type_str {
33        "i8" | "u8" | "i16" | "u16" | "i32" | "u32" | "i64" | "u64" | "bool" | "char" => {
34            format!("{} as i64", expr_str)
35        }
36        "f32" | "f64" => format!("{} as f64", expr_str),
37        _ => format!("{}.into_opengauss_type()", expr_str),
38    })
39    .unwrap()
40}
41
42/* openGauss bindgen transforms a native Rust function into a form compilable to Wasm
43** and immediately usable as a openGauss user-defined function.
44** It follows the following ABI rules for openGauss types (INTEGER, FLOAT, STRING, BLOB, NULL):
45**  1. integers are passed as i64
46**  2. floats are passed as f64
47**  3. Strings, blobs and nulls are pointers,
48**     with the first byte indicating the type - SQLITE_TEXT, SQLITE_BLOB or SQLITE_NULL
49**     and then:
50**       - strings are encoded as null-terminated strings
51**       - blobs are encoded as [4 bytes of size information][data]
52**       - nulls are encoded as nothing, because the type byte already indicates it's null
53*/
54#[proc_macro_attribute]
55pub fn opengauss_bindgen(_attrs: TokenStream, item: TokenStream) -> TokenStream {
56    let input = match parse::<ItemFn>(item) {
57        Ok(i) => i,
58        Err(_) => {
59            return TokenStream::from(
60                syn::Error::new(
61                    Span::call_site(),
62                    "opengauss_bindgen operates on function definitions only",
63                )
64                .to_compile_error(),
65            )
66        }
67    };
68
69    let mut native_sig = input.sig.clone();
70    let mut generated_sig = input.sig.clone();
71    native_sig.ident = syn::Ident::new(
72        format!("__opengauss_native_{}", generated_sig.ident).as_str(),
73        Span::call_site(),
74    );
75    // Translate parameter types
76    for raw_param in &mut generated_sig.inputs {
77        if let &mut FnArg::Typed(ref mut param) = raw_param {
78            param.ty = gen_opengauss_type(&param.ty);
79            if let &mut Pat::Ident(ref mut id) = param.pat.borrow_mut() {
80                id.mutability = Option::None;
81            }
82        }
83    }
84    // Translate the return type
85    let mut return_type_str = String::new();
86    if let &syn::ReturnType::Type(_, ref ty) = &generated_sig.output {
87        return_type_str = ty.to_owned().into_token_stream().to_string();
88        generated_sig.output =
89            syn::ReturnType::Type(syn::token::RArrow::default(), gen_opengauss_type(ty));
90    }
91
92    // Copy the native function body
93    let native_block = &input.block;
94    let mut generated_block = syn::Block {
95        brace_token: input.block.brace_token,
96        stmts: Vec::<Stmt>::new(),
97    };
98
99    // Generate the exported function body
100    let mut raw_ret_expr =
101        parse_str::<syn::Expr>(&format!("{}()", &native_sig.ident.to_string())).unwrap();
102    if let &mut syn::Expr::Call(ref mut call) = &mut raw_ret_expr {
103        for raw_param in &native_sig.inputs {
104            if let &FnArg::Typed(ref param) = raw_param {
105                if let &Pat::Ident(ref id) = param.pat.borrow() {
106                    let from_expr =
107                        parse_str::<syn::Expr>(&gen_from_expr(&param.ty, &id.ident.to_string()))
108                            .unwrap();
109                    call.args.push(from_expr);
110                }
111            }
112        }
113    }
114    let ret_expr = gen_into_expr(&return_type_str, raw_ret_expr);
115    generated_block.stmts.push(Stmt::Expr(ret_expr));
116
117    TokenStream::from(quote! {
118        #native_sig
119        #native_block
120
121        #[no_mangle]
122        pub #generated_sig
123        #generated_block
124    })
125}