rbatis_codegen/codegen/
parser_html.rs

1use proc_macro2::{Ident, Span};
2use quote::{quote, ToTokens};
3use std::collections::{BTreeMap, HashMap};
4use std::fs::File;
5use std::io::Read;
6use std::path::PathBuf;
7use syn::{ItemFn, LitStr};
8use url::Url;
9
10use crate::codegen::loader_html::{load_html, Element};
11use crate::codegen::proc_macro::TokenStream;
12use crate::codegen::string_util::find_convert_string;
13use crate::codegen::ParseArgs;
14
15use crate::error::Error;
16
17/// load a Map<id,Element>
18pub fn load_mapper_map(html: &str) -> Result<BTreeMap<String, Element>, Error> {
19    let datas = load_mapper_vec(html)?;
20    let mut sql_map = BTreeMap::new();
21    let datas = include_replace(datas, &mut sql_map);
22    let mut m = BTreeMap::new();
23    for x in datas {
24        if let Some(v) = x.attrs.get("id") {
25            m.insert(v.to_string(), x);
26        }
27    }
28    Ok(m)
29}
30
31/// load a Vec<Element>
32pub fn load_mapper_vec(html: &str) -> Result<Vec<Element>, Error> {
33    let datas = load_html(html).map_err(|e| Error::from(e.to_string()))?;
34    let mut mappers = vec![];
35    for x in datas {
36        if x.tag.eq("mapper") {
37            for x in x.childs {
38                mappers.push(x);
39            }
40        } else {
41            mappers.push(x);
42        }
43    }
44    Ok(mappers)
45}
46
47/// parse html to function TokenStream
48pub fn parse_html(html: &str, fn_name: &str, ignore: &mut Vec<String>) -> proc_macro2::TokenStream {
49    let html = html
50        .replace("\\\"", "\"")
51        .replace("\\n", "\n")
52        .trim_start_matches("\"")
53        .trim_end_matches("\"")
54        .to_string();
55    let datas = load_mapper_map(&html).expect(&format!("laod html={} fail", html));
56    match datas.into_iter().next() {
57        None => {
58            panic!("html not find fn:{}", fn_name);
59        }
60        Some((_, v)) => {
61            let node = parse_html_node(vec![v], ignore, fn_name);
62            return node;
63        }
64    }
65}
66
67fn include_replace(htmls: Vec<Element>, sql_map: &mut BTreeMap<String, Element>) -> Vec<Element> {
68    let mut results = vec![];
69    for mut x in htmls {
70        match x.tag.as_str() {
71            "sql" => {
72                sql_map.insert(
73                    x.attrs
74                        .get("id")
75                        .expect("[rbatis-codegen] <sql> element must have id!")
76                        .clone(),
77                    x.clone(),
78                );
79            }
80            "include" => {
81                let ref_id = x
82                    .attrs
83                    .get("refid")
84                    .expect(
85                        "[rbatis-codegen] <include> element must have attr <include refid=\"\">!",
86                    )
87                    .clone();
88                let url;
89                if ref_id.contains("://") {
90                    url = Url::parse(&ref_id).expect(&format!(
91                        "[rbatis-codegen] parse <include refid=\"{}\"> fail!",
92                        ref_id
93                    ));
94                } else {
95                    url = Url::parse(&format!("current://current?refid={}", ref_id)).expect(
96                        &format!(
97                            "[rbatis-codegen] parse <include refid=\"{}\"> fail!",
98                            ref_id
99                        ),
100                    );
101                }
102                let mut manifest_dir =
103                    std::env::var("CARGO_MANIFEST_DIR").expect("Failed to read CARGO_MANIFEST_DIR");
104                manifest_dir.push_str("/");
105
106                let path = url.host_str().unwrap_or_default().to_string()
107                    + url.path().trim_end_matches("/").trim_end_matches("\\");
108                let mut file_path = PathBuf::from(&path);
109                if file_path.is_relative() {
110                    file_path = PathBuf::from(format!("{}{}", manifest_dir, path));
111                }
112
113                match url.scheme() {
114                    "file" => {
115                        let mut ref_id = ref_id.clone();
116                        let mut have_ref_id = false;
117                        for (k, v) in url.query_pairs() {
118                            if k.eq("refid") {
119                                ref_id = v.to_string();
120                                have_ref_id = true;
121                            }
122                        }
123                        if !have_ref_id {
124                            panic!("not find ref_id on url {}", ref_id);
125                        }
126                        let mut f = File::open(&file_path).expect(&format!(
127                            "[rbatis-codegen] can't find file='{}',url='{}' ",
128                            file_path.to_str().unwrap_or_default(),
129                            url
130                        ));
131                        let mut html = String::new();
132                        f.read_to_string(&mut html).expect("read fail");
133                        let datas = load_mapper_vec(&html).expect("read fail");
134                        let mut not_find = true;
135                        for element in datas {
136                            if element.tag.eq("sql") && element.attrs.get("id").eq(&Some(&ref_id)) {
137                                x = element.clone();
138                                not_find = false;
139                            }
140                        }
141                        if not_find {
142                            panic!(
143                                "not find ref_id={} on file={}",
144                                ref_id,
145                                file_path.to_str().unwrap_or_default()
146                            );
147                        }
148                    }
149                    "current" => {
150                        let mut ref_id_pair = ref_id.to_string();
151                        for (k, v) in url.query_pairs() {
152                            if k.eq("refid") {
153                                ref_id_pair = v.to_string();
154                            }
155                        }
156                        let element = sql_map
157                            .get(ref_id_pair.as_str())
158                            .expect(&format!(
159                                "[rbatis-codegen] can not find element <include refid=\"{}\"> !",
160                                ref_id
161                            ))
162                            .clone();
163                        x = element;
164                    }
165                    _scheme => {
166                        panic!("unimplemented scheme <include refid=\"{}\">", ref_id)
167                    }
168                }
169            }
170            _ => match x.attrs.get("id") {
171                None => {}
172                Some(id) => {
173                    if !id.is_empty() {
174                        sql_map.insert(id.clone(), x.clone());
175                    }
176                }
177            },
178        }
179        if x.childs.len() != 0 {
180            x.childs = include_replace(x.childs.clone(), sql_map);
181        }
182        results.push(x);
183    }
184    return results;
185}
186
187fn parse_html_node(
188    htmls: Vec<Element>,
189    ignore: &mut Vec<String>,
190    fn_name: &str,
191) -> proc_macro2::TokenStream {
192    let mut methods = quote!();
193    let fn_impl = parse(&htmls, &mut methods, ignore, fn_name);
194    let token = quote! {
195        #methods
196        #fn_impl
197    };
198    token
199}
200
201/// gen rust code
202fn parse(
203    arg: &Vec<Element>,
204    methods: &mut proc_macro2::TokenStream,
205    ignore: &mut Vec<String>,
206    fn_name: &str,
207) -> proc_macro2::TokenStream {
208    let mut body = quote! {};
209    let fix_sql = quote! {};
210    for x in arg {
211        match x.tag.as_str() {
212            "mapper" => {
213                return parse(&x.childs, methods, ignore, fn_name);
214            }
215            "sql" => {
216                let code_sql = parse(&x.childs, methods, ignore, fn_name);
217                body = quote! {
218                            #body
219                            #code_sql
220                };
221            }
222            "include" => {
223                return parse(&x.childs, methods, ignore, fn_name);
224            }
225            "continue" => {
226                impl_continue(x, &mut body, ignore);
227            }
228            "break" => {
229                impl_break(x, &mut body, ignore);
230            }
231            "" => {
232                let mut string_data = remove_extra(&x.data);
233                let convert_list = find_convert_string(&string_data);
234                let mut formats_value = quote! {};
235                let mut replace_num = 0;
236                for (k, v) in convert_list {
237                    let method_impl = crate::codegen::func::impl_fn(
238                        &body.to_string(),
239                        "",
240                        &format!("\"{}\"", k),
241                        false,
242                        ignore,
243                    );
244                    if v.starts_with("#") {
245                        string_data = string_data.replacen(&v, &"?", 1);
246                        body = quote! {
247                            #body
248                            args.push(rbs::to_value(#method_impl).unwrap_or_default());
249                        };
250                    } else {
251                        string_data = string_data.replacen(&v, &"{}", 1);
252                        if formats_value.to_string().trim().ends_with(",") == false {
253                            formats_value = quote!(#formats_value,);
254                        }
255                        formats_value = quote!(
256                            #formats_value
257                            &#method_impl.string()
258                        );
259                        replace_num += 1;
260                    }
261                }
262                if !string_data.is_empty() {
263                    if replace_num == 0 {
264                        body = quote!(
265                           #body
266                           sql.push_str(#string_data);
267                        );
268                    } else {
269                        body = quote!(
270                           #body
271                           sql.push_str(&format!(#string_data #formats_value));
272                        );
273                    }
274                }
275            }
276            "if" => {
277                let test_value = x
278                    .attrs
279                    .get("test")
280                    .expect(&format!("{} element must be have test field!", x.tag));
281                let mut if_tag_body = quote! {};
282                if x.childs.len() != 0 {
283                    if_tag_body = parse(&x.childs, methods, ignore, fn_name);
284                }
285                impl_if(
286                    test_value,
287                    if_tag_body,
288                    &mut body,
289                    methods,
290                    quote! {},
291                    ignore,
292                );
293            }
294            "trim" => {
295                let empty_string = String::new();
296                let prefix = x.attrs.get("prefix").unwrap_or(&empty_string).to_string();
297                let suffix = x.attrs.get("suffix").unwrap_or(&empty_string).to_string();
298                let prefix_overrides = x
299                    .attrs
300                    .get("start")
301                    .unwrap_or_else(|| x.attrs.get("prefixOverrides").unwrap_or(&empty_string))
302                    .to_string();
303                let suffix_overrides = x
304                    .attrs
305                    .get("end")
306                    .unwrap_or_else(|| x.attrs.get("suffixOverrides").unwrap_or(&empty_string))
307                    .to_string();
308                impl_trim(
309                    &prefix,
310                    &suffix,
311                    &prefix_overrides,
312                    &suffix_overrides,
313                    x,
314                    &mut body,
315                    arg,
316                    methods,
317                    ignore,
318                    fn_name,
319                );
320            }
321            "bind" => {
322                let name = x
323                    .attrs
324                    .get("name")
325                    .expect("<bind> must be have name!")
326                    .to_string();
327                let value = x
328                    .attrs
329                    .get("value")
330                    .expect("<bind> element must be have value!")
331                    .to_string();
332                let method_impl = crate::codegen::func::impl_fn(
333                    &body.to_string(),
334                    "",
335                    &format!("\"{}\"", value),
336                    false,
337                    ignore,
338                );
339                let lit_str = LitStr::new(&name, Span::call_site());
340                body = quote! {
341                    #body
342                    //bind
343                    if arg[#lit_str] == rbs::Value::Null{
344                        arg.insert(rbs::Value::String(#lit_str.to_string()), rbs::Value::Null);
345                    }
346                    arg[#lit_str] = rbs::to_value(#method_impl).unwrap_or_default();
347                };
348            }
349
350            "where" => {
351                impl_trim(
352                    " where ",
353                    " ",
354                    " |and |or ",
355                    " | and| or",
356                    x,
357                    &mut body,
358                    arg,
359                    methods,
360                    ignore,
361                    fn_name,
362                );
363                body = quote! {
364                    #body
365                    //check if body empty ends with `where`
366                    sql = sql.trim_end_matches(" where  ").to_string();
367                };
368            }
369
370            "choose" => {
371                let mut inner_body = quote! {};
372                for x in &x.childs {
373                    if x.tag.ne("when") && x.tag.ne("otherwise") {
374                        panic!("choose node's childs must be when node and otherwise node!");
375                    }
376                    if x.tag.eq("when") {
377                        let test_value = x
378                            .attrs
379                            .get("test")
380                            .expect(&format!("{} element must be have test field!", x.tag));
381                        let mut if_tag_body = quote! {};
382                        if x.childs.len() != 0 {
383                            if_tag_body = parse(&x.childs, methods, ignore, fn_name);
384                        }
385                        impl_if(
386                            test_value,
387                            if_tag_body,
388                            &mut inner_body,
389                            methods,
390                            quote! {return sql;},
391                            ignore,
392                        );
393                    }
394                    if x.tag.eq("otherwise") {
395                        let child_body = parse(&x.childs, methods, ignore, fn_name);
396                        impl_otherwise(child_body, &mut inner_body, methods, ignore);
397                    }
398                }
399                let cup = x.child_string_cup() + 1000;
400                body = quote! {
401                  #body
402                  sql.push_str(&|| -> String {
403                           let mut sql = String::with_capacity(#cup);
404                           #inner_body
405                           return sql;
406                  }());
407                }
408            }
409
410            "foreach" => {
411                let empty_string = String::new();
412
413                let def_item = "item".to_string();
414                let def_index = "index".to_string();
415
416                let collection = x
417                    .attrs
418                    .get("collection")
419                    .unwrap_or(&empty_string)
420                    .to_string();
421                let mut item = x.attrs.get("item").unwrap_or(&def_item).to_string();
422                let mut findex = x.attrs.get("index").unwrap_or(&def_index).to_string();
423                let open = x.attrs.get("open").unwrap_or(&empty_string).to_string();
424                let close = x.attrs.get("close").unwrap_or(&empty_string).to_string();
425                let separator = x
426                    .attrs
427                    .get("separator")
428                    .unwrap_or(&empty_string)
429                    .to_string();
430
431                if item.is_empty() || item == "_" {
432                    item = def_item;
433                }
434                if findex.is_empty() || findex == "_" {
435                    findex = def_index;
436                }
437                let mut ignores = ignore.clone();
438                ignores.push(findex.to_string());
439                ignores.push(item.to_string());
440
441                let impl_body = parse(&x.childs, methods, &mut ignores, fn_name);
442                let method_impl = crate::codegen::func::impl_fn(
443                    &body.to_string(),
444                    "",
445                    &format!("\"{}\"", collection),
446                    false,
447                    ignore,
448                );
449                body = quote! {
450                    #body
451                };
452                let mut open_impl = quote! {};
453                if !open.is_empty() {
454                    open_impl = quote! {
455                    sql.push_str(#open);
456                    };
457                }
458                let mut close_impl = quote! {};
459                if !close.is_empty() {
460                    close_impl = quote! {sql.push_str(#close);};
461                }
462                let item_ident = Ident::new(&item, Span::call_site());
463                let index_ident = Ident::new(&findex, Span::call_site());
464                let mut split_code = quote! {};
465                let mut split_code_trim = quote! {};
466                if !separator.is_empty() {
467                    split_code = quote! {
468                        sql.push_str(#separator);
469                    };
470                    split_code_trim = quote! {
471                       sql = sql.trim_end_matches(#separator).to_string();
472                    };
473                }
474                body = quote! {
475                    #body
476                    #open_impl
477                    for (ref #index_ident,#item_ident) in #method_impl {
478                        #impl_body
479                        #split_code
480                    }
481                    #split_code_trim
482                    #close_impl
483                };
484                body = quote! {
485                    #body
486                };
487            }
488
489            "set" => {
490                let collection = x.attrs.get("collection");
491                let skip_null = x.attrs.get("skip_null");
492                let skips = x.attrs.get("skips");
493                if let Some(collection) = collection {
494                    let elements = make_sets(collection, skip_null, skips.unwrap_or(&"id".to_string()));
495                    let code = parse(&elements, methods, ignore, fn_name);
496                    body = quote! {
497                         #body
498                         #code
499                    };
500                } else {
501                    impl_trim(
502                        " set ", " ", " |,", " |,", x, &mut body, arg, methods, ignore, fn_name,
503                    );
504                }
505            }
506
507            "select" => {
508                let method_name = Ident::new(fn_name, Span::call_site());
509                let child_body = parse(&x.childs, methods, ignore, fn_name);
510                let cup = x.child_string_cup() + 1000;
511                let push_count = child_body.to_string().matches("args.push").count();
512                let select = quote! {
513                    pub fn #method_name (mut arg: rbs::Value, _tag: char) -> (String,Vec<rbs::Value>) {
514                       use rbatis_codegen::ops::*;
515                       let mut sql = String::with_capacity(#cup);
516                       let mut args = Vec::with_capacity(#push_count);
517                       #child_body
518                       #fix_sql
519                       return (sql,args);
520                    }
521                };
522                body = quote! {
523                    #body
524                    #select
525                };
526            }
527            "update" => {
528                let method_name = Ident::new(fn_name, Span::call_site());
529                let child_body = parse(&x.childs, methods, ignore, fn_name);
530                let cup = x.child_string_cup() + 1000;
531                let push_count = child_body.to_string().matches("args.push").count();
532                let select = quote! {
533                    pub fn #method_name (mut arg: rbs::Value, _tag: char) -> (String,Vec<rbs::Value>) {
534                       use rbatis_codegen::ops::*;
535                       let mut sql = String::with_capacity(#cup);
536                       let mut args = Vec::with_capacity(#push_count);
537                       #child_body
538                       #fix_sql
539                       return (sql,args);
540                    }
541                };
542                body = quote! {
543                    #body
544                    #select
545                };
546            }
547            "insert" => {
548                let method_name = Ident::new(fn_name, Span::call_site());
549                let child_body = parse(&x.childs, methods, ignore, fn_name);
550                let cup = x.child_string_cup() + 1000;
551                let push_count = child_body.to_string().matches("args.push").count();
552                let select = quote! {
553                    pub fn #method_name (mut arg: rbs::Value, _tag: char) -> (String,Vec<rbs::Value>) {
554                       use rbatis_codegen::ops::*;
555                       let mut sql = String::with_capacity(#cup);
556                       let mut args = Vec::with_capacity(#push_count);
557                       #child_body
558                       #fix_sql
559                       return (sql,args);
560                    }
561                };
562                body = quote! {
563                    #body
564                    #select
565                };
566            }
567            "delete" => {
568                let method_name = Ident::new(fn_name, Span::call_site());
569                let child_body = parse(&x.childs, methods, ignore, fn_name);
570                let cup = x.child_string_cup() + 1000;
571                let push_count = child_body.to_string().matches("args.push").count();
572                let select = quote! {
573                    pub fn #method_name (mut arg: rbs::Value, _tag: char) -> (String,Vec<rbs::Value>) {
574                       use rbatis_codegen::ops::*;
575                       let mut sql = String::with_capacity(#cup);
576                       let mut args = Vec::with_capacity(#push_count);
577                       #child_body
578                       #fix_sql
579                       return (sql,args);
580                    }
581                };
582                body = quote! {
583                    #body
584                    #select
585                };
586            }
587            _ => {}
588        }
589    }
590
591    body.into()
592}
593
594/// make <set>
595fn make_sets(collection: &str, skip_null: Option<&String>, skips: &str) -> Vec<Element> {
596    let mut is_skip_null = true;
597    if let Some(skip_null_value) = skip_null {
598        if skip_null_value.eq("false") {
599            is_skip_null = false;
600        }
601    }
602    let skip_strs: Vec<&str> = skips.split(',').collect();
603    let mut skip_element = vec![];
604    for x in skip_strs {
605        let element = Element {
606            tag: "if".to_string(),
607            data: "".to_string(),
608            attrs: {
609                let mut attr = HashMap::new();
610                attr.insert("test".to_string(), format!("k == '{}'", x.to_string()));
611                attr
612            },
613            childs: vec![Element {
614                tag: "continue".to_string(),
615                data: "".to_string(),
616                attrs: Default::default(),
617                childs: vec![],
618            }],
619        };
620        skip_element.push(element);
621    }
622    let mut for_each_body = vec![];
623    for x in skip_element {
624        for_each_body.push(x);
625    }
626    if is_skip_null {
627        for_each_body.push(Element {
628            tag: "if".to_string(),
629            data: "".to_string(),
630            attrs: {
631                let mut attr = HashMap::new();
632                attr.insert("test".to_string(), "v == null".to_string());
633                attr
634            },
635            childs: vec![
636                Element {
637                    tag: "continue".to_string(),
638                    data: "".to_string(),
639                    attrs: Default::default(),
640                    childs: vec![],
641                },
642            ],
643        });
644    }
645    for_each_body.push(Element {
646        tag: "".to_string(),
647        data: "${k}=#{v},".to_string(),
648        attrs: Default::default(),
649        childs: vec![],
650    });
651    let mut elements = vec![];
652    elements.push(Element {
653        tag: "trim".to_string(),
654        data: "".to_string(),
655        attrs: {
656            let mut attr = HashMap::new();
657            attr.insert("prefix".to_string(), " set ".to_string());
658            attr.insert("suffix".to_string(), " ".to_string());
659            attr.insert("start".to_string(), " ".to_string());
660            attr.insert("end".to_string(), " ".to_string());
661            attr
662        },
663        childs: vec![Element {
664            tag: "trim".to_string(),
665            data: "".to_string(),
666            attrs: {
667                let mut attr = HashMap::new();
668                attr.insert("prefix".to_string(), "".to_string());
669                attr.insert("suffix".to_string(), "".to_string());
670                attr.insert("start".to_string(), ",".to_string());
671                attr.insert("end".to_string(), ",".to_string());
672                attr
673            },
674            childs: vec![Element {
675                tag: "foreach".to_string(),
676                data: "".to_string(),
677                attrs: {
678                    let mut attr = HashMap::new();
679                    attr.insert("collection".to_string(), collection.to_string());
680                    attr.insert("index".to_string(), "k".to_string());
681                    attr.insert("item".to_string(), "v".to_string());
682                    attr
683                },
684                childs: for_each_body,
685            }],
686        }],
687    });
688    elements
689}
690
691fn remove_extra(txt: &str) -> String {
692    let txt = txt.trim().replace("\\r", "");
693    let lines: Vec<&str> = txt.split("\n").collect();
694    let mut data = String::with_capacity(txt.len());
695    let mut index = 0;
696    for line in &lines {
697        let mut line = line.trim_start().trim_end();
698        if line.starts_with("`") {
699            line = line.trim_start_matches("`");
700        }
701        if line.ends_with("`") {
702            line = line.trim_end_matches("`");
703        }
704        data.push_str(line);
705        if index + 1 < lines.len() {
706            data.push_str("\n");
707        }
708        index += 1;
709    }
710    if data.starts_with("`") && data.ends_with("`") {
711        data = data
712            .trim_start_matches("`")
713            .trim_end_matches("`")
714            .to_string();
715    }
716    data = data.replace("``", "").to_string();
717    data
718}
719
720fn impl_continue(_x: &Element, body: &mut proc_macro2::TokenStream, _ignore: &mut Vec<String>) {
721    *body = quote! {
722         #body
723         continue
724    };
725}
726
727fn impl_break(_x: &Element, body: &mut proc_macro2::TokenStream, _ignore: &mut Vec<String>) {
728    *body = quote! {
729         #body
730         break
731    };
732}
733
734fn impl_if(
735    test_value: &str,
736    if_tag_body: proc_macro2::TokenStream,
737    body: &mut proc_macro2::TokenStream,
738    _methods: &mut proc_macro2::TokenStream,
739    appends: proc_macro2::TokenStream,
740    ignore: &mut Vec<String>,
741) {
742    let method_impl = crate::codegen::func::impl_fn(
743        &body.to_string(),
744        "",
745        &format!("\"{}\"", test_value),
746        false,
747        ignore,
748    );
749    *body = quote! {
750          #body
751          if #method_impl.to_owned().into() {
752             #if_tag_body
753             #appends
754          }
755    };
756}
757
758fn impl_otherwise(
759    child_body: proc_macro2::TokenStream,
760    body: &mut proc_macro2::TokenStream,
761    _methods: &mut proc_macro2::TokenStream,
762    _ignore: &mut Vec<String>,
763) {
764    *body = quote!(
765           #body
766           #child_body
767    );
768}
769
770fn impl_trim(
771    prefix: &str,
772    suffix: &str,
773    start: &str,
774    end: &str,
775    x: &Element,
776    body: &mut proc_macro2::TokenStream,
777    _arg: &Vec<Element>,
778    methods: &mut proc_macro2::TokenStream,
779    ignore: &mut Vec<String>,
780    fn_name: &str,
781) {
782    let trim_body = parse(&x.childs, methods, ignore, fn_name);
783    let prefixes: Vec<&str> = start.split("|").collect();
784    let suffixes: Vec<&str> = end.split("|").collect();
785    let have_trim = prefixes.len() != 0 && suffixes.len() != 0;
786    let cup = x.child_string_cup();
787    let mut trims = quote! {
788         let mut sql= String::with_capacity(#cup);
789         #trim_body
790         sql=sql
791    };
792    for x in prefixes {
793        trims = quote! {
794            #trims
795            .trim_start_matches(#x)
796        }
797    }
798    for x in suffixes {
799        trims = quote! {
800            #trims
801            .trim_end_matches(#x)
802        }
803    }
804    if !prefix.is_empty() {
805        *body = quote! {
806           #body
807           sql.push_str(#prefix);
808        };
809    }
810    if have_trim {
811        *body = quote! {
812           #body
813           sql.push_str(&{#trims.to_string(); sql });
814        };
815    }
816    if !suffix.is_empty() {
817        *body = quote! {
818           #body
819           sql.push_str(#suffix);
820        };
821    }
822}
823
824pub fn impl_fn_html(m: &ItemFn, args: &ParseArgs) -> TokenStream {
825    let fn_name = m.sig.ident.to_string();
826    if args.sqls.len() == 0 {
827        panic!(
828            "[rbatis-codegen] #[html_sql()] must have html_data, for example: {}",
829            stringify!(#[html_sql(r#"<select id="select_by_condition">`select * from biz_activity</select>"#)])
830        );
831    }
832    let html_data = args.sqls[0].to_token_stream().to_string();
833    let t = parse_html(&html_data, &fn_name, &mut vec![]);
834    return t.into();
835}