taitan_orm_parser/template_parser/structs/
template_part.rs

1use crate::template_parser::to_sql::{SqlSegment, ToSqlSegment};
2use nom::{
3    branch::alt,
4    bytes::complete::{tag, take_until},
5    character::complete::multispace0,
6    combinator::{map, opt},
7    sequence::delimited,
8    IResult,
9};
10use std::fmt::Display;
11
12#[derive(PartialEq, Debug, Clone, Copy)]
13pub enum Modifier {
14    Plus,
15    Minus,
16    Tilde,
17}
18impl Display for Modifier {
19    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20        match self {
21            Modifier::Plus => write!(f, "+"),
22            Modifier::Minus => write!(f, "-"),
23            Modifier::Tilde => write!(f, "~"),
24        }
25    }
26}
27
28#[derive(Debug, PartialEq, Clone)]
29pub struct EndBlock {
30    pub name: String,
31    pub start_modifier: Option<Modifier>,
32    pub end_modifier: Option<Modifier>,
33}
34
35impl Display for EndBlock {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        let start_modifier_str = if let Some(start_modifier) = &self.start_modifier {
38            start_modifier.to_string()
39        } else {
40            "".to_string()
41        };
42        let end_modifier_str = if let Some(end_modifier) = &self.end_modifier {
43            end_modifier.to_string()
44        } else {
45            "".to_string()
46        };
47        write!(
48            f,
49            "{{%{} {} {}%}}",
50            start_modifier_str, self.name, end_modifier_str
51        )
52    }
53}
54
55#[derive(Debug, PartialEq, Clone)]
56pub struct StartBlock {
57    pub name: String,
58    pub start_modifier: Option<Modifier>,
59    pub end_modifier: Option<Modifier>,
60    pub expr: String,
61}
62
63impl Display for StartBlock {
64    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65        let start_modifier_str = if let Some(start_modifier) = &self.start_modifier {
66            start_modifier.to_string()
67        } else {
68            "".to_string()
69        };
70        let end_modifier_str = if let Some(end_modifier) = &self.end_modifier {
71            end_modifier.to_string()
72        } else {
73            "".to_string()
74        };
75        write!(
76            f,
77            "{{%{} {} {} {}%}}",
78            start_modifier_str, self.name, self.expr, end_modifier_str
79        )
80    }
81}
82
83#[derive(Debug, PartialEq, Clone)]
84pub enum TemplatePart {
85    Expression(String), // {{ }} 表达式及其过滤器
86    ControlBlock {
87        start_block: StartBlock,
88        content: String,
89        end_block: EndBlock,
90    }, // {% %} 控制块,包括macro
91    Call(String),       // {% call %} call 语句
92    Comment(String),    // {# #}注释块
93}
94
95impl Display for TemplatePart {
96    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97        match self {
98            TemplatePart::ControlBlock {
99                start_block,
100                content,
101                end_block,
102            } => {
103                write!(f, "{}{}{}", start_block, content, end_block)
104            }
105            TemplatePart::Expression(expression) => {
106                write!(f, "{{ {} }}", expression)
107            }
108            TemplatePart::Call(call_expr) => {
109                write!(f, "{{% call {} %}}", call_expr)
110            }
111            TemplatePart::Comment(comment) => write!(f, ""),
112        }
113    }
114}
115
116impl TemplatePart {
117    pub fn parse(input: &str) -> IResult<&str, TemplatePart> {
118        let (input, part) = delimited(
119            multispace0,
120            alt((
121                map(parse_call, |part| part),
122                map(parse_comment, |part| part),
123                map(parse_expression, |part| part),
124                map(parse_control_block, |part| part),
125            )),
126            multispace0,
127        )(input)?;
128        Ok((input, part))
129    }
130}
131
132fn parse_expression(input: &str) -> IResult<&str, TemplatePart> {
133    let (input, _) = tag("{{")(input)?;
134    let (input, expr) = take_until("}}")(input)?;
135    let (input, _) = tag("}}")(input)?;
136    Ok((input, TemplatePart::Expression(expr.trim().to_string())))
137}
138
139// 辅助函数:生成一个解析器,该解析器可以识别并消耗特定的结束标记
140fn take_until_and_consume<F, R>(parser: F) -> impl Fn(&str) -> IResult<&str, String>
141where
142    F: Fn(&str) -> IResult<&str, R>,
143{
144    move |input: &str| {
145        let mut buffer = String::new();
146        let mut remaining = input;
147
148        loop {
149            match parser(remaining) {
150                Ok((_, _)) => {
151                    // 找到了结束标记,停止循环
152                    break;
153                }
154                Err(_) => {
155                    if remaining.is_empty() {
156                        // 如果没有剩余输入且未找到结束标记,返回错误
157                        return Err(nom::Err::Error(nom::error::Error::new(
158                            input,
159                            nom::error::ErrorKind::TakeUntil,
160                        )));
161                    }
162
163                    // 将当前字符添加到缓冲区并继续
164                    buffer.push(remaining.chars().next().unwrap());
165                    remaining = &remaining[1..];
166                }
167            }
168        }
169        Ok((remaining, buffer))
170    }
171}
172fn parse_modifier(input: &str) -> IResult<&str, Modifier> {
173    let (input, modifier_str) = alt((tag("+"), tag("-"), tag("~")))(input)?;
174    let modifier = match modifier_str {
175        "+" => Modifier::Plus,
176        "-" => Modifier::Minus,
177        "~" => Modifier::Tilde,
178        _ => unreachable!(),
179    };
180    Ok((input, modifier))
181}
182
183fn parse_start_brace(input: &str) -> IResult<&str, Option<Modifier>> {
184    let (input, _) = tag("{%")(input)?;
185    let (input, _) = multispace0(input)?; // 允许空格
186    let (input, modifier) = opt(parse_modifier)(input)?;
187    Ok((input, modifier))
188}
189
190fn parse_end_brace(input: &str) -> IResult<&str, Option<Modifier>> {
191    let (input, modifier) = opt(parse_modifier)(input)?;
192    let (input, _) = multispace0(input)?; // 允许空格
193    let (input, _) = tag("%}")(input)?;
194    Ok((input, modifier))
195}
196
197fn parse_start_block_with_name<'a>(
198    input: &'a str,
199    block_name: &'static str,
200) -> IResult<&'a str, StartBlock> {
201    let (input, start_modifier) = parse_start_brace(input)?;
202    let (input, _) = multispace0(input)?; // 允许空格
203    let (input, parsed_block_name) = tag(block_name)(input)?;
204    let (input, _) = multispace0(input)?; // 允许空格
205    let (input, expr) = take_until_and_consume(parse_end_brace)(input)?; // 表达式可以包含任意字符
206    let (input, _) = multispace0(input)?; // 允许空格
207    let (input, end_modifier) = parse_end_brace(input)?; // 解析可能的结束修饰符
208
209    Ok((
210        input,
211        StartBlock {
212            name: parsed_block_name.to_string(),
213            start_modifier,
214            end_modifier,
215            expr: expr.trim().to_string(),
216        },
217    ))
218}
219
220fn parse_start_block(input: &str) -> IResult<&str, StartBlock> {
221    alt((
222        |i| parse_start_block_with_name(i, "if"),
223        |i| parse_start_block_with_name(i, "match"),
224        |i| parse_start_block_with_name(i, "for"),
225        |i| parse_start_block_with_name(i, "macro"),
226        |i| parse_start_block_with_name(i, "filter"),
227        |i| parse_start_block_with_name(i, "block"),
228    ))(input)
229}
230
231fn parse_end_block_with_name<'a>(
232    input: &'a str,
233    block_name: &'static str,
234) -> IResult<&'a str, EndBlock> {
235    let (input, start_modifier) = parse_start_brace(input)?;
236    let (input, _) = multispace0(input)?; // 允许空格
237    let (input, _) = tag(block_name)(input)?;
238    let (input, _) = multispace0(input)?; // 允许空格
239    let (input, end_modifier) = parse_end_brace(input)?; // 表达式可以包含任意字符
240    Ok((
241        input,
242        EndBlock {
243            name: block_name.to_string(),
244            start_modifier,
245            end_modifier,
246        },
247    ))
248}
249
250fn parse_end_block(input: &str) -> IResult<&str, EndBlock> {
251    alt((
252        |i| parse_end_block_with_name(i, "endif"),
253        |i| parse_end_block_with_name(i, "endmatch"),
254        |i| parse_end_block_with_name(i, "endfor"),
255        |i| parse_end_block_with_name(i, "endmacro"),
256        |i| parse_end_block_with_name(i, "endfilter"),
257        |i| parse_end_block_with_name(i, "endblock"),
258    ))(input)
259}
260
261fn parse_control_block(input: &str) -> IResult<&str, TemplatePart> {
262    // 解析控制块开始标记
263    let (input, start_block) = parse_start_block(input)?;
264
265    let (input, control_content) = take_until_and_consume(parse_end_block)(input)?;
266
267    let (input, end_block) = parse_end_block(input)?;
268
269    Ok((
270        input,
271        TemplatePart::ControlBlock {
272            start_block,
273            content: control_content.trim().to_string(),
274            end_block,
275        },
276    ))
277}
278
279fn parse_call(input: &str) -> IResult<&str, TemplatePart> {
280    let (input, _) = tag("{% call ")(input)?;
281    let (input, expr) = take_until("%}")(input)?;
282    let (input, _) = tag("%}")(input)?;
283    Ok((input, TemplatePart::Call(expr.trim().to_string())))
284}
285
286fn parse_comment(input: &str) -> IResult<&str, TemplatePart> {
287    let (input, _) = tag("{#")(input)?;
288    let (input, comment) = take_until("#}")(input)?;
289    let (input, _) = tag("#}")(input)?;
290    Ok((input, TemplatePart::Comment(comment.to_string())))
291}
292
293// fn parse_template_part(input: &str) -> IResult<&str, TemplatePart> {
294//     let (input, part) = delimited(
295//         multispace0,
296//         alt((
297//             map(parse_call, |part| part),
298//             map(parse_comment, |part| part),
299//             map(parse_expression, |part| part),
300//             map(parse_control_block, |part| part),
301//         )),
302//         multispace0,
303//     )(input)?;
304//     Ok((input, part))
305// }
306
307// fn parse_template(input: &str) -> IResult<&str, Vec<TemplatePart>> {
308//     many0(parse_template_part)(input)
309// }
310
311#[cfg(test)]
312mod tests {
313    use super::*;
314
315    #[test]
316    fn test_parse_end_brace() {
317        let template = "%}";
318        let (remaining, end_block) = parse_end_brace(template).unwrap();
319        assert_eq!(end_block, None);
320
321        let template = "- %}";
322        let (remaining, end_block) = parse_end_brace(template).unwrap();
323        assert_eq!(end_block, Some(Modifier::Minus));
324
325        let template = "~ %}";
326        let (remaining, end_block) = parse_end_brace(template).unwrap();
327        assert_eq!(end_block, Some(Modifier::Tilde));
328    }
329
330    #[test]
331    fn test_parse_start_block() {
332        let template = r#"{% if active %}"#;
333        let (remaining, parsed) = parse_start_block(template).unwrap();
334        let expected = StartBlock {
335            name: "if".to_string(),
336            start_modifier: None,
337            end_modifier: None,
338            expr: "active".to_string(),
339        };
340        assert_eq!(expected, parsed);
341        let template = r#"{%~ for active set as test -%}"#;
342        let (remaining, parsed) = parse_start_block(template).unwrap();
343        let expected = StartBlock {
344            name: "for".to_string(),
345            start_modifier: Some(Modifier::Tilde),
346            end_modifier: Some(Modifier::Minus),
347            expr: "active set as test".to_string(),
348        };
349        assert_eq!(expected, parsed);
350    }
351
352    #[test]
353    fn test_parse_end_block() {
354        let template = r#"{% endif %}"#;
355        let (remaining, parsed) = parse_end_block(template).unwrap();
356        assert_eq!(
357            parsed,
358            EndBlock {
359                name: String::from("endif"),
360                start_modifier: None,
361                end_modifier: None
362            }
363        );
364
365        let template = r#"{% if active %}
366            You are active.
367        {% endif -%}"#;
368        let (remaining, _) = parse_start_block(template).unwrap();
369        assert_eq!(
370            remaining,
371            "\n            You are active.\n        {% endif -%}"
372        );
373        let (remaining, parsed) = take_until_and_consume(parse_end_block)(remaining).unwrap();
374        assert_eq!(parsed, "\n            You are active.\n        ");
375    }
376
377    #[test]
378    fn test_parse_template_part() {
379        let template = r#"{{ name | capitalize }}"#;
380        let (remaining, parsed_template_parts) = TemplatePart::parse(template).unwrap();
381        let expected = TemplatePart::Expression("name | capitalize".to_string());
382        assert_eq!(parsed_template_parts, expected);
383
384        let template = r#"{% if active %}
385            You are active.
386        {% endif -%}"#;
387        let (remaining, parsed_template_parts) = TemplatePart::parse(template).unwrap();
388        let expected = TemplatePart::ControlBlock {
389            start_block: StartBlock {
390                name: "if".to_string(),
391                start_modifier: None,
392                end_modifier: None,
393                expr: "active".to_string(),
394            },
395            content: "You are active.".to_string(),
396            end_block: EndBlock {
397                name: "endif".to_string(),
398                start_modifier: None,
399                end_modifier: Some(Modifier::Minus),
400            },
401        };
402        assert_eq!(parsed_template_parts, expected);
403
404        let template = r#"
405        {% for item in items %}
406            Item: {{ item | upper }}
407        {% endfor %}"#;
408        let (remaining, parsed_template_parts) = TemplatePart::parse(template).unwrap();
409        let expected = TemplatePart::ControlBlock {
410            start_block: StartBlock {
411                name: "for".to_string(),
412                start_modifier: None,
413                end_modifier: None,
414                expr: "item in items".to_string(),
415            },
416            content: "Item: {{ item | upper }}".to_string(),
417            end_block: EndBlock {
418                name: "endfor".to_string(),
419                start_modifier: None,
420                end_modifier: None,
421            },
422        };
423        assert_eq!(parsed_template_parts, expected);
424
425        let template = r#"{% call macro %}"#;
426        let (remaining, parsed_template_parts) = TemplatePart::parse(template).unwrap();
427        let expected = TemplatePart::Call("macro".to_string());
428        assert_eq!(parsed_template_parts, expected);
429
430        let template = r#"
431        {# This is a comment #}"#;
432        let (remaining, parsed_template_parts) = TemplatePart::parse(template).unwrap();
433        let expected = TemplatePart::Comment(" This is a comment ".to_string());
434        assert_eq!(parsed_template_parts, expected);
435    }
436}