taitan_orm_parser/template_parser/structs/
placeholder.rs

1use std::str::FromStr;
2use crate::template_parser::structs::variable::VariableChain;
3use crate::template_parser::to_sql::{SqlSegment, ToSqlSegment};
4use nom::branch::alt;
5use nom::bytes::complete::tag;
6use nom::character::complete::{digit1, multispace0};
7use nom::combinator::map;
8use nom::sequence::{delimited, preceded, tuple};
9use nom::IResult;
10
11#[derive(Debug, PartialEq, Eq, Clone)]
12pub enum RawPlaceholder {
13    QuestionMark,
14    Indexed(usize),
15    Named(String),
16}
17
18impl RawPlaceholder {
19    pub fn parse(input: &str) -> IResult<&str, RawPlaceholder> {
20        alt((
21            map(tag("?"), |_| RawPlaceholder::QuestionMark),
22            map(preceded(tag("$"), digit1), |d| {
23                RawPlaceholder::Indexed(usize::from_str(d).unwrap())
24            }),
25            map(
26                preceded(tag(":"), preceded(multispace0, VariableChain::parse)),
27                |v| RawPlaceholder::Named(v.to_string()),
28            ),
29        ))(input)
30    }
31}
32
33impl ToSqlSegment for RawPlaceholder {
34    fn gen_sql_segment(&self) -> SqlSegment {
35        match self {
36            RawPlaceholder::QuestionMark => SqlSegment::Simple("?".to_string()),
37            RawPlaceholder::Indexed(i) => SqlSegment::Simple(format!("${}", i)),
38            RawPlaceholder::Named(i) => SqlSegment::Simple(i.to_string()),
39        }
40    }
41}
42
43#[derive(Debug, PartialEq, Eq, Clone)]
44pub enum Placeholder {
45    Raw(RawPlaceholder),
46    Dollar(VariableChain),
47    Hash(VariableChain),
48    At(VariableChain),
49}
50impl Placeholder {
51    pub fn parse(input: &str) -> IResult<&str, Placeholder> {
52        alt((
53            map(RawPlaceholder::parse, Placeholder::Raw),
54            delimited(
55                preceded(tag("$"), preceded(multispace0, tag("{"))),
56                preceded(multispace0, map(VariableChain::parse, Placeholder::Dollar)),
57                preceded(multispace0, tag("}")),
58            ),
59            delimited(
60                preceded(tag("#"), preceded(multispace0, tag("{"))),
61                preceded(multispace0, map(VariableChain::parse, Placeholder::Hash)),
62                preceded(multispace0, tag("}")),
63            ),
64            delimited(
65                preceded(tag("@"), preceded(multispace0, tag("{"))),
66                preceded(multispace0, map(VariableChain::parse, Placeholder::At)),
67                preceded(multispace0, tag("}")),
68            ),
69        ))(input)
70    }
71}
72
73impl ToSqlSegment for Placeholder {
74    fn gen_sql_segment(&self) -> SqlSegment {
75        match self {
76            Placeholder::Dollar(p) => SqlSegment::Dollar(p.to_string()),
77            Placeholder::Hash(p) => SqlSegment::Hash(p.to_string()),
78            Placeholder::At(p) => SqlSegment::At(p.to_string()),
79            Placeholder::Raw(p)=>p.gen_sql_segment(),
80        }
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87    use crate::template_parser::structs::variable::Variable;
88    #[test]
89    fn placeholder_parser_spec_001() {
90        let template = "@{ hello. name. attr1 }";
91        let (_, placeholder) = Placeholder::parse(template).unwrap();
92        let variable_chain = vec![
93            Variable::Simple("hello".to_string()),
94            Variable::Simple("name".to_string()),
95            Variable::Simple("attr1".to_string()),
96        ];
97        assert_eq!(
98            placeholder,
99            Placeholder::At(VariableChain::new(variable_chain))
100        );
101
102        let template = "#{ [ hello ]. \" name \". ` attr1 ` }";
103        let (_, placeholder) = Placeholder::parse(template).unwrap();
104        let variable_chain = vec![
105            Variable::Brackets("hello".to_string()),
106            Variable::DoubleQuote("name".to_string()),
107            Variable::Backquote("attr1".to_string()),
108        ];
109        assert_eq!(
110            placeholder,
111            Placeholder::Hash(VariableChain::new(variable_chain))
112        );
113
114        let template = "${ [ hello ]. name . ` attr1 ` }";
115        let (_, placeholder) = Placeholder::parse(template).unwrap();
116        let variable_chain = vec![
117            Variable::Brackets("hello".to_string()),
118            Variable::Simple("name".to_string()),
119            Variable::Backquote("attr1".to_string()),
120        ];
121        assert_eq!(
122            placeholder,
123            Placeholder::Dollar(VariableChain::new(variable_chain))
124        );
125    }
126
127    #[test]
128    fn placeholder_parser_spec_002() {
129        let template = "@{ hello. name. attr1. }";
130        let parse_result = Placeholder::parse(template);
131        assert!(parse_result.is_err());
132        if let Err(nom::Err::Error(error)) = parse_result {
133            assert_eq!(error.to_string(), "error Tag at: . }");
134        }
135    }
136}