rbatis_codegen/codegen/
parser_html.rs1use proc_macro2::TokenStream;
2use quote::{quote, ToTokens};
3use std::collections::BTreeMap;
4use syn::ItemFn;
5
6use crate::codegen::loader_html::{load_html, Element};
7use crate::codegen::proc_macro::TokenStream as MacroTokenStream;
8use crate::codegen::string_util::{concat_str, find_convert_string};
9use crate::codegen::syntax_tree_html::*;
10use crate::codegen::ParseArgs;
11use crate::error::Error;
12
13const SQL_TAG: &str = "sql";
15const INCLUDE_TAG: &str = "include";
16pub(crate) const MAPPER_TAG: &str = "mapper";
17const IF_TAG: &str = "if";
18const TRIM_TAG: &str = "trim";
19const BIND_TAG: &str = "bind";
20const WHERE_TAG: &str = "where";
21const CHOOSE_TAG: &str = "choose";
22const WHEN_TAG: &str = "when";
23const OTHERWISE_TAG: &str = "otherwise";
24const FOREACH_TAG: &str = "foreach";
25const SET_TAG: &str = "set";
26const CONTINUE_TAG: &str = "continue";
27const BREAK_TAG: &str = "break";
28const SELECT_TAG: &str = "select";
29const UPDATE_TAG: &str = "update";
30const INSERT_TAG: &str = "insert";
31const DELETE_TAG: &str = "delete";
32
33pub fn load_mapper_map(html: &str) -> Result<BTreeMap<String, Element>, Error> {
35 let elements = load_mapper_vec(html)?;
36 let mut sql_map = BTreeMap::new();
37 let processed_elements = include_replace(elements, &mut sql_map);
38
39 let mut m = BTreeMap::new();
40 for x in processed_elements {
41 if let Some(v) = x.attrs.get("id") {
42 m.insert(v.to_string(), x);
43 }
44 }
45 Ok(m)
46}
47
48pub fn load_mapper_vec(html: &str) -> Result<Vec<Element>, Error> {
50 let elements = load_html(html).map_err(|e| Error::from(e.to_string()))?;
51
52 let mut mappers = Vec::new();
53 for element in elements {
54 if element.tag == MAPPER_TAG {
55 mappers.extend(element.childs);
56 } else {
57 mappers.push(element);
58 }
59 }
60
61 Ok(mappers)
62}
63
64fn include_replace(
66 elements: Vec<Element>,
67 sql_map: &mut BTreeMap<String, Element>,
68) -> Vec<Element> {
69 elements
70 .into_iter()
71 .map(|mut element| {
72 match element.tag.as_str() {
73 SQL_TAG => {
74 let id = element
75 .attrs
76 .get("id")
77 .expect("[rbatis-codegen] <sql> element must have id!");
78 sql_map.insert(id.clone(), element.clone());
79 }
80 INCLUDE_TAG => {
81 element = IncludeTagNode::from_element(&element).process_include(sql_map);
82 }
83 _ => {
84 if let Some(id) = element.attrs.get("id").filter(|id| !id.is_empty()) {
85 sql_map.insert(id.clone(), element.clone());
86 }
87 }
88 }
89
90 if !element.childs.is_empty() {
91 element.childs = include_replace(element.childs, sql_map);
92 }
93
94 element
95 })
96 .collect()
97}
98
99pub fn parse_html(html: &str, fn_name: &str, ignore: &mut Vec<String>) -> TokenStream {
101 let processed_html = html
102 .replace("\\\"", "\"")
103 .replace("\\n", "\n")
104 .trim_matches('"')
105 .to_string();
106
107 let elements = load_mapper_map(&processed_html)
108 .unwrap_or_else(|_| panic!("Failed to load html: {}", processed_html));
109
110 let (_, element) = elements
111 .into_iter()
112 .next()
113 .unwrap_or_else(|| panic!("HTML not found for function: {}", fn_name));
114
115 parse_html_node(vec![element], ignore, fn_name)
116}
117
118fn parse_html_node(elements: Vec<Element>, ignore: &mut Vec<String>, fn_name: &str) -> TokenStream {
120 let mut methods = quote!();
121 let fn_impl = parse_elements(&elements, &mut methods, ignore, fn_name);
122 quote! { #methods #fn_impl }
123}
124
125fn parse_elements(
127 elements: &[Element],
128 methods: &mut TokenStream,
129 ignore: &mut Vec<String>,
130 fn_name: &str,
131) -> TokenStream {
132 let mut body = quote! {};
133
134 let mut context = NodeContext {
136 methods,
137 fn_name,
138 child_parser: parse_elements,
139 };
140
141 for element in elements {
142 match element.tag.as_str() {
143 "" => {
144 handle_text_element(element, &mut body, ignore);
146 }
147 MAPPER_TAG => {
148 let node = MapperTagNode::from_element(element);
149 body = node.generate_tokens(&mut context, ignore);
150 }
151 SQL_TAG => {
152 let node = SqlTagNode::from_element(element);
153 let code = node.generate_tokens(&mut context, ignore);
154 body = quote! { #body #code };
155 }
156 INCLUDE_TAG => {
157 let node = IncludeTagNode::from_element(element);
160 let code = node.generate_tokens(&mut context, ignore);
161 body = quote! { #body #code };
162 }
163 CONTINUE_TAG => {
164 let node = ContinueTagNode::from_element(element);
165 let code = node.generate_tokens(&mut context, ignore);
166 body = quote! { #body #code };
167 }
168 BREAK_TAG => {
169 let node = BreakTagNode::from_element(element);
170 let code = node.generate_tokens(&mut context, ignore);
171 body = quote! { #body #code };
172 }
173 IF_TAG => {
174 let node = IfTagNode::from_element(element);
175 let code = node.generate_tokens(&mut context, ignore);
176 body = quote! { #body #code };
177 }
178 TRIM_TAG => {
179 let node = TrimTagNode::from_element(element);
180 let code = node.generate_tokens(&mut context, ignore);
181 body = quote! { #body #code };
182 }
183 BIND_TAG => {
184 let node = BindTagNode::from_element(element);
185 let code = node.generate_tokens(&mut context, ignore);
186 body = quote! { #body #code };
187 }
188 WHERE_TAG => {
189 let node = WhereTagNode::from_element(element);
190 let code = node.generate_tokens(&mut context, ignore);
191 body = quote! { #body #code };
192 }
193 CHOOSE_TAG => {
194 let node = ChooseTagNode::from_element(element);
195 let code = node.generate_tokens(&mut context, ignore);
196 body = quote! { #body #code };
197 }
198 FOREACH_TAG => {
199 let node = ForeachTagNode::from_element(element);
200 let code = node.generate_tokens(&mut context, ignore);
201 body = quote! { #body #code };
202 }
203 SET_TAG => {
204 let node = SetTagNode::from_element(element);
205 let code = node.generate_tokens(&mut context, ignore);
206 body = quote! { #body #code };
207 }
208 SELECT_TAG => {
209 let node = SelectTagNode::from_element(element);
210 let code = node.generate_tokens(&mut context, ignore);
211 body = quote! { #body #code };
212 }
213 UPDATE_TAG => {
214 let node = UpdateTagNode::from_element(element);
215 let code = node.generate_tokens(&mut context, ignore);
216 body = quote! { #body #code };
217 }
218 INSERT_TAG => {
219 let node = InsertTagNode::from_element(element);
220 let code = node.generate_tokens(&mut context, ignore);
221 body = quote! { #body #code };
222 }
223 DELETE_TAG => {
224 let node = DeleteTagNode::from_element(element);
225 let code = node.generate_tokens(&mut context, ignore);
226 body = quote! { #body #code };
227 }
228 WHEN_TAG => {}
229 OTHERWISE_TAG => {}
230 _ => {}
231 }
232 }
233
234 body
235}
236
237fn handle_text_element(element: &Element, body: &mut TokenStream, ignore: &mut Vec<String>) {
239 let mut string_data = remove_extra(&element.data);
240 let convert_list = find_convert_string(&string_data);
241
242 let mut formats_value = quote! {};
243 let mut replace_num = 0;
244
245 for (k, v) in convert_list {
246 let method_impl = crate::codegen::func::impl_fn(
247 &body.to_string(),
248 "",
249 &format!("\"{}\"", k),
250 false,
251 ignore,
252 );
253
254 if v.starts_with('#') {
255 string_data = string_data.replacen(&v, "?", 1);
256 *body = quote! {
257 #body
258 args.push(rbs::value(#method_impl).unwrap_or_default());
259 };
260 } else {
261 string_data = string_data.replacen(&v, "{}", 1);
262 if !formats_value.to_string().trim().ends_with(',') {
263 formats_value = quote!(#formats_value,);
264 }
265 formats_value = quote!(#formats_value &#method_impl.string());
266 replace_num += 1;
267 }
268 }
269
270 if !string_data.is_empty() {
271 *body = if replace_num == 0 {
272 quote! { #body rbatis_codegen::codegen::string_util::concat_str(&mut sql, #string_data); }
273 } else {
274 quote! { #body rbatis_codegen::codegen::string_util::concat_str(&mut sql, &format!(#string_data #formats_value)); }
275 };
276 }
277}
278
279fn remove_extra(text: &str) -> String {
281 let text = text.trim().replace("\\r", "");
282 let lines: Vec<&str> = text.split('\n').collect();
283
284 let mut data = String::with_capacity(text.len());
285 for (i, line) in lines.iter().enumerate() {
286 let mut line = line.trim();
287 line = line.trim_start_matches('`').trim_end_matches('`');
288
289 let list: Vec<&str> = line.split("``").collect();
290 let mut text = String::with_capacity(line.len());
291 for s in list {
292 concat_str(&mut text, s);
293 }
294 data.push_str(&text);
295 if i + 1 < lines.len() {
296 data.push('\n');
297 }
298 }
299
300 data
301}
302
303pub fn impl_fn_html(m: &ItemFn, args: &ParseArgs) -> MacroTokenStream {
305 let fn_name = m.sig.ident.to_string();
306
307 if args.sqls.is_empty() {
308 panic!(
309 "[rbatis-codegen] #[html_sql()] must have html_data, for example: {}",
310 stringify!(#[html_sql(r#"<select id="select_by_condition">`select * from biz_activity</select>"#)])
311 );
312 }
313
314 let html_data = args.sqls[0].to_token_stream().to_string();
315 parse_html(&html_data, &fn_name, &mut vec![]).into()
316}