1#![feature(proc_macro_span)]
2
3mod sql_fragment;
7mod sql_expand;
8
9use proc_macro::TokenStream;
10use sql_expand::SqlExpand;
11use sql_fragment::{STATIC_SQL_FRAGMENT_MAP, SqlFragment};
12use syn::{parse_macro_input, Token};
13use std::{collections::HashMap, sync::RwLock, path::PathBuf};
14use quote::quote;
15
16use sql_fragment::get_sql_fragment;
17
18#[allow(dead_code)]
20#[derive(Debug)]
21pub(crate) struct DyClosure {
22 executor_info: ExecutorInfo,
23 dto_info: DtoInfo,
24 sql_name: Option<String>,
25 ret_type: Option<syn::Path>, body: String,
27 source_file: PathBuf,
28}
29
30#[derive(Debug)]
31enum RefKind {
32 Immutable,
33 Mutable,
34 None
35}
36
37
38#[derive(Debug)]
39struct DtoInfo {
40 src: Option<syn::Ident>,
41 ref_kind: RefKind,
42}
43
44impl DtoInfo {
45 pub fn new(src: Option<syn::Ident>, ref_kind: RefKind) -> Self {
46 Self {
47 src,
48 ref_kind,
49 }
50 }
51
52 pub fn gen_token(&self) -> proc_macro2::TokenStream {
53 if let Some(_) = self.src {
54 let mut rst = match self.ref_kind {
55 RefKind::Immutable => quote!(&),
56 RefKind::Mutable => quote!(&mut),
57 RefKind::None => quote!(),
58 };
59
60 let dto = &self.src;
61 rst.extend(quote!(#dto));
62
63 rst.into()
64 } else {
65 quote!()
66 }
67 }
68}
69
70
71#[derive(Debug)]
72struct ExecutorInfo {
73 src: syn::Ident,
74 ref_kind: RefKind,
75 is_deref: bool,
76}
77
78impl ExecutorInfo {
79 pub fn new(src: syn::Ident, ref_kind: RefKind, is_deref: bool) -> Self {
80 Self {
81 src,
82 ref_kind,
83 is_deref,
84 }
85 }
86
87 pub fn gen_token(&self) -> proc_macro2::TokenStream {
88 let mut rst = match self.ref_kind {
89 RefKind::Immutable => quote!(&),
90 RefKind::Mutable => quote!(&mut),
91 RefKind::None => quote!(),
92 };
93
94 if self.is_deref {
95 rst.extend(quote!(*))
96 }
97
98 let executor = &self.src;
99 rst.extend(quote!(#executor));
100
101 quote!((#rst))
102 }
103}
104
105impl syn::parse::Parse for DyClosure {
106 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
107 dotenv::dotenv().ok();
109
110 input.parse::<syn::Token!(|)>()?;
112
113 let executor_ref_kind: RefKind;
115 match input.parse::<syn::Token!(&)>() {
116 Err(_) => executor_ref_kind = RefKind::None,
117 Ok(_) => match input.parse::<syn::Token!(mut)>() {
118 Err(_) => executor_ref_kind = RefKind::Immutable,
119 Ok(_) => executor_ref_kind = RefKind::Mutable,
120 }
121 }
122
123 let is_executor_deref: bool;
125 let executor: syn::Ident;
126 match input.parse::<syn::Token!(*)>() {
127 Ok(_) => is_executor_deref = true,
128 Err(_) => is_executor_deref = false,
129 }
130 match input.parse::<syn::Ident>() {
131 Err(e) => return Err(e),
132 Ok(ex) => executor = ex,
133 }
134
135 let sql_name: Option<String>;
137 let dto: Option<syn::Ident>;
138 let dto_ref_kind: RefKind;
139 match input.parse::<syn::Token!(|)>() {
140 Ok(_) => {
141 sql_name = None;
142 dto = None;
143 dto_ref_kind = RefKind::None;
144 },
145 Err(_) => match input.parse::<syn::Token!(,)>() {
146 Err(e) => return Err(e),
147 Ok(_) => {
148 match input.parse::<syn::Token!(_)>() {
150 Ok(_) => {
151 dto = None;
152 dto_ref_kind = RefKind::None;
153 },
154 Err(_) => {
155 match input.parse::<syn::Token!(&)>() {
156 Ok(_) => match input.parse::<syn::Token!(mut)>(){
157 Ok(_) => dto_ref_kind = RefKind::Mutable,
158 Err(_) => dto_ref_kind = RefKind::Immutable,
159 },
160 Err(_) => dto_ref_kind = RefKind::None,
161 }
162 match input.parse::<syn::Ident>() {
163 Err(e) => return Err(e),
164 Ok(d) => dto = Some(d),
165 }
166 }
167 }
168
169 match input.parse::<syn::Token!(|)>() {
171 Ok(_) => sql_name = None,
173 Err(_) => match input.parse::<syn::Token!(,)>() {
175 Err(e) => return Err(e),
176 Ok(_) => {
178 match input.parse::<syn::Token!(_)>() {
179 Ok(_) => { sql_name = None },
180 Err(_) => match input.parse::<syn::LitStr>() {
181 Ok(s) => sql_name = Some(s.value()),
182 Err(_) => return Err(syn::Error::new(proc_macro2::Span::call_site(), "need specify the sql_name")),
183 }
184 }
185 input.parse::<syn::Token!(|)>()?;
187 }
188 }
189 }
190 }
191 }
192 }
193
194 let ret_type:Option<syn::Path>;
196 match input.parse::<syn::Token!(->)>() {
197 Ok(_) => match input.parse::<syn::Path>() {
199 Ok(p) => ret_type = Some(p),
200 Err(_) =>
201 return Err(syn::Error::new(proc_macro2::Span::call_site(), "Need specify the return type")),
202 }
203 Err(_) => ret_type = None,
204 };
205
206 let body = parse_body(input)?;
208 let body: Vec<String> = body.split('\n').into_iter().map(|f| f.trim().to_owned()).collect();
209 let body = body.join(" ").to_owned();
210
211 let span: proc_macro::Span = input.span().unwrap();
213 let source_file = span.source_file().path();
214
215 let executor_info = ExecutorInfo::new(executor, executor_ref_kind, is_executor_deref);
216 let dto_info = DtoInfo::new(dto, dto_ref_kind);
217
218 let dsf = DyClosure { executor_info, dto_info, sql_name, ret_type, body, source_file };
219 Ok(dsf)
222 }
223}
224
225fn parse_body(input: &syn::parse::ParseBuffer) -> Result<String, syn::Error> {
227 let body_buf;
228 syn::braced!(body_buf in input);
230
231 let ts = body_buf.cursor().token_stream().into_iter();
232 let mut sql = String::new();
233 for it in ts {
234 match it {
235 proc_macro2::TokenTree::Group(_) => {
236 return Err(syn::Error::new(input.span(), "error not support group in sql".to_owned()));
237 },
238 proc_macro2::TokenTree::Ident(_) => {
239 let v: syn::Ident = body_buf.parse()?;
240 let sql_fragment = get_sql_fragment(&v.to_string());
241
242 if let Some(s) = sql_fragment {
243 sql.push_str(&s);
244 } else {
245 return Err(syn::Error::new(input.span(), "error not found sql identity".to_owned()));
246 }
247 },
248 proc_macro2::TokenTree::Punct(v) => {
249 if v.to_string() == "+" {
250 body_buf.parse::<Token!(+)>()?;
251 } else {
252 return Err(syn::Error::new(input.span(), "error only support '+' expr".to_owned()));
253 }
254 },
255 proc_macro2::TokenTree::Literal(_) => {
256 let rst: syn::LitStr = body_buf.parse()?;
257
258 sql.push_str(&rst.value());
259 },
260 };
261 }
262
263 Ok(sql)
264}
265
266#[proc_macro]
306pub fn fetch_all(input: TokenStream) -> TokenStream {
307 let st = syn::parse_macro_input!(input as DyClosure);
309
310 if st.ret_type.is_none() { panic!("return type can't be null.") }
312
313 match SqlExpand.fetch_all(&st) {
314 Ok(ret) => ret.into(),
315 Err(e) => e.into_compile_error().into(),
316 }
317}
318
319#[proc_macro]
339pub fn fetch_one(input: TokenStream) -> TokenStream {
340 let st = syn::parse_macro_input!(input as DyClosure);
342
343 if st.ret_type.is_none() { panic!("return type can't be null.") }
345
346 match SqlExpand.fetch_one(&st) {
347 Ok(ret) => ret.into(),
348 Err(e) => e.into_compile_error().into(),
349 }
350}
351
352#[proc_macro]
368pub fn fetch_scalar(input: TokenStream) -> TokenStream {
369 let st = syn::parse_macro_input!(input as DyClosure);
371 if st.ret_type.is_none() { panic!("return type can't be null.") }
372
373 match SqlExpand.fetch_scalar(&st) {
374 Ok(ret) => ret.into(),
375 Err(e) => e.into_compile_error().into(),
376 }
377}
378
379#[proc_macro]
398pub fn execute(input: TokenStream) -> TokenStream {
399 let st = syn::parse_macro_input!(input as DyClosure);
401
402 match SqlExpand.execute(&st) {
403 Ok(ret) => ret.into(),
404 Err(e) => e.into_compile_error().into(),
405 }
406}
407
408#[proc_macro]
429pub fn insert(input: TokenStream) -> TokenStream {
430 let st = syn::parse_macro_input!(input as DyClosure);
432 if st.ret_type.is_none() { panic!("return type can't be null.") }
433
434 match SqlExpand.insert(&st) {
435 Ok(ret) => ret.into(),
436 Err(e) => e.into_compile_error().into(),
437 }
438}
439
440#[proc_macro]
457pub fn sql(input: TokenStream) -> TokenStream {
458 let st = parse_macro_input!(input as SqlFragment);
459 let cache = STATIC_SQL_FRAGMENT_MAP.get_or_init(|| {
460 RwLock::new(HashMap::new())
461 });
462
463 cache.write().unwrap().insert(st.name, st.value.to_string());
464
465 quote!().into()
466}
467
468#[proc_macro]
492pub fn page(input: TokenStream) -> TokenStream {
493 let st = syn::parse_macro_input!(input as DyClosure);
495 if st.ret_type.is_none() { panic!("return type can't be null.") }
496
497 match SqlExpand.page(&st) {
498 Ok(ret) => ret.into(),
499 Err(e) => e.into_compile_error().into(),
500 }
501}