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