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