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
use proc_macro::TokenStream;
use quote::quote;
#[allow(dead_code)]
#[derive(Debug)]
struct SqlClosure {
dto: syn::Ident,
dialect: syn::Ident,
body: String,
}
#[proc_macro]
pub fn sql(input: TokenStream) -> TokenStream {
let st = syn::parse_macro_input!(input as SqlClosure);
match expand(&st) {
Ok(ret) => ret.into(),
Err(e) => e.into_compile_error().into(),
}
}
fn expand(st: &SqlClosure) -> syn::Result<proc_macro2::TokenStream> {
let dto = &st.dto;
let body = &st.body;
let dialect = &st.dialect.to_string();
let template_id = dysql::md5(body);
ramhorns::Template::new(body.clone()).unwrap();
let (tmp_sql, param_strings) = dysql::extract_params(&body, dysql::SqlDialect::from(dialect.to_owned()));
if tmp_sql == "".to_owned() {
return Err(syn::Error::new(proc_macro2::Span::call_site(), format!("Parse sql error: {} ", body)))
}
let param_idents: Vec<_> = param_strings.iter().map( |p| proc_macro2::Ident::new(p, proc_macro2::Span::call_site()) ).collect();
let ret = quote!(
{
let mut param_values: Vec<&(dyn tokio_postgres::types::ToSql + Sync)> = Vec::new();
let sql_tpl = ramhorns::Template::new(#body).unwrap();
let sql_tpl = match dysql::get_sql_template(#template_id) {
Some(tpl) => tpl,
None => dysql::put_sql_template(#template_id, #body).expect("Unexpected error when put_sql_template"),
};
let sql_rendered = unsafe{(*sql_tpl).render(&#dto)};
let rst = dysql::extract_params(&sql_rendered, dysql::SqlDialect::from(#dialect.to_owned()));
let (sql, param_names) = rst;
for i in 0..param_names.len() {
#(
if param_names[i] == #param_strings {
param_values.push(&#dto.#param_idents);
}
)*
}
(sql, param_values)
}
);
Ok(ret)
}
impl syn::parse::Parse for SqlClosure {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
input.parse::<syn::Token!(|)>()?;
let dto: syn::Ident = input.parse()?;
input.parse::<syn::Token!(|)>()?;
let dialect: syn::Ident = match input.parse::<syn::Token!(->)>() {
Ok(_) => input.parse()?,
Err(_) => syn::Ident::new(&dysql::SqlDialect::postgres.to_string(), input.span()),
};
let body_buf;
syn::braced!(body_buf in input);
let body: syn::LitStr = body_buf.parse()?;
let body = body.value();
let body:Vec<_> = body.split("\n").map(|f| f.trim()).collect();
let body = body.join(" ");
Ok(SqlClosure { dto, dialect, body })
}
}