xitca_postgres_codegen/
lib.rs1use quote::quote;
5use sqlparser::{dialect::PostgreSqlDialect, parser::Parser};
6use syn::{
7 parse::{Parse, ParseStream},
8 spanned::Spanned,
9 token::Comma,
10 Expr, ExprReference, Lit, LitStr,
11};
12
13#[proc_macro]
14pub fn sql(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
15 let Query { sql, exprs, types } = syn::parse_macro_input!(input as Query);
16
17 quote! {
18 ::xitca_postgres::statement::Statement::unnamed(#sql, &[#(#types),*])
19 .bind_dyn(&[#(#exprs),*])
20 }
21 .into()
22}
23
24struct Query {
25 sql: LitStr,
26 exprs: Vec<ExprReference>,
27 types: Vec<proc_macro2::TokenStream>,
28}
29
30impl Parse for Query {
31 fn parse(input: ParseStream) -> syn::Result<Self> {
32 let sql = input.parse::<LitStr>()?;
33
34 Parser::parse_sql(&PostgreSqlDialect {}, &sql.value())
35 .map_err(|e| syn::Error::new(sql.span(), e.to_string()))?;
36
37 let mut exprs = Vec::new();
38 let mut types = Vec::new();
39
40 while input.parse::<Comma>().is_ok() {
41 let expr = input.parse::<ExprReference>()?;
42 let Expr::Lit(lit) = expr.expr.as_ref() else {
43 return Err(syn::Error::new(
44 expr.span(),
45 "example can only handle literal expression",
46 ));
47 };
48 let ty = match lit.lit {
49 Lit::Str(_) => quote! { ::xitca_postgres::types::Type::TEXT },
50 Lit::Int(_) => quote! { ::xitca_postgres::types::Type::INT4 },
51 ref lit => {
52 return Err(syn::Error::new(
53 lit.span(),
54 "example can only handle i32 and String type as parameter",
55 ))
56 }
57 };
58 types.push(ty);
59 exprs.push(expr);
60 }
61
62 Ok(Self { sql, exprs, types })
63 }
64}