taitan_orm_parser/template_parser/structs/
atomic.rs

1use crate::template::MaybeValue;
2use crate::template_parser::structs::bool_value::Bool;
3use crate::template_parser::structs::connect_op::ConnectOp;
4use crate::template_parser::structs::number::Number;
5use crate::template_parser::structs::operators::Operator;
6use crate::template_parser::structs::placeholder::Placeholder;
7use crate::template_parser::structs::sign::Sign;
8use crate::template_parser::structs::template_part::TemplatePart;
9use crate::template_parser::structs::text::Text;
10use crate::template_parser::structs::values::GenericValue;
11use crate::template_parser::structs::variable::VariableChain;
12use crate::template_parser::to_sql::SqlSegment::Simple;
13use crate::template_parser::to_sql::{SqlSegment, ToSqlSegment};
14use nom::branch::alt;
15use nom::bytes::complete::tag_no_case;
16use nom::character::complete::multispace0;
17use nom::combinator::{map, not};
18use nom::sequence::preceded;
19use nom::IResult;
20use std::fmt::{Display, Formatter};
21use taitan_orm_tracing::debug;
22use crate::template_parser::structs::keyword::PostgresKeyword;
23
24
25
26
27#[derive(Debug, Clone, PartialEq)]
28pub enum Atomic {
29    Number(Number),
30    Text(Text),
31    Bool(Bool),
32    Operator(Operator), // 各类操作符+-*/% like in = > < <> !=
33    Maybe(MaybeValue),  // 可能是Number/Text/Bool/Operator
34    Sign(Sign),         // 各种特殊符号,例如括号()[]{}等
35}
36
37impl Atomic {
38    pub fn parse(input: &str) -> IResult<&str, Atomic> {
39        debug!("Atomic parse({})", &input);
40        let (remaining, parsed) = alt((
41            map(Number::parse, Atomic::Number),
42            map(Text::parse, Atomic::Text),
43            map(Bool::parse, Atomic::Bool),
44            map(Sign::parse, Atomic::Sign), // 需要保证+ - * 先被解析为Sign, + - 可能是number修饰符,也可能是算术操作符,*可能是算术操作符,也可能是星号
45            map(Operator::parse, Atomic::Operator),
46            map(MaybeValue::parse, Atomic::Maybe),
47        ))(input)?;
48        debug!("Atomic parse -> {:?}", &parsed);
49        Ok((remaining, parsed))
50    }
51
52    pub fn is_binary_op(&self) -> bool {
53        match self {
54            Atomic::Operator(_) => true,
55            _ => false,
56        }
57    }
58    pub fn is_operand(&self) -> bool {
59        match self {
60            Atomic::Operator(_) | Atomic::Sign(_) => false,
61            _ => true,
62        }
63    }
64
65    pub fn extract_binary_op(&self) -> Option<Operator> {
66        if let Atomic::Operator(o) = self {
67            return Some(o.clone());
68        }
69        None
70    }
71
72    pub fn extract_left_bracket(&self) -> Option<Sign> {
73        if let Atomic::Sign(Sign::Bracket(c)) = self {
74            if c == &'(' {
75                return Some(Sign::Bracket('('));
76            }
77        }
78        None
79    }
80    pub fn extract_right_bracket(&self) -> Option<Sign> {
81        if let Atomic::Sign(Sign::Bracket(c)) = self {
82            if c == &')' {
83                return Some(Sign::Bracket(')'));
84            }
85        }
86        None
87    }
88}
89
90impl ToSqlSegment for Atomic {
91    fn gen_sql_segment(&self) -> SqlSegment {
92        match self {
93            Atomic::Sign(s) => SqlSegment::Simple(s.to_string()),
94            Atomic::Maybe(m) => SqlSegment::Simple(m.gen_sql_segment().to_sql(false).to_string()),
95            Atomic::Operator(b) => SqlSegment::Simple(b.to_string()),
96            Atomic::Bool(b) => SqlSegment::Simple(b.to_string()),
97            Atomic::Text(t) => SqlSegment::Simple(t.to_string()),
98            Atomic::Number(n) => SqlSegment::Simple(n.to_string()),
99        }
100    }
101}
102
103#[derive(Debug, Clone, PartialEq)]
104pub struct AtomicStream {
105    pub atomics: Vec<Atomic>,
106}
107
108impl AtomicStream {
109    pub fn parse(input: &str) -> Result<Self, String> {
110        debug!("SqlTemplate::parse({})", input);
111        let mut atomics = Vec::new();
112        let mut remainder = input;
113        loop {
114            let parse_result = preceded(multispace0, Atomic::parse)(remainder);
115            match parse_result {
116                Ok((remaining, parsed)) => {
117                    debug!("SqlTemplate::parse({})->{:?}", remaining, parsed);
118                    atomics.push(parsed);
119                    remainder = remaining;
120                }
121                Err(err_msg) => {
122                    debug!("SqlTemplate::parse error: {}", err_msg);
123                    let err_msg = format!("failed to parse atomic: {}", input);
124                    return Err(err_msg);
125                }
126            }
127
128            if remainder.is_empty() {
129                break;
130            }
131        }
132        Ok(AtomicStream { atomics })
133    }
134}
135
136#[cfg(test)]
137mod atomic_tests {
138    use super::*;
139    use crate::template_parser::structs::template_part::{EndBlock, StartBlock};
140    use crate::template_parser::structs::values::MaybeValue;
141    use crate::template_parser::structs::variable::Variable;
142    #[test]
143    fn atomic_parser_spec_001() {
144        let template = "#{hello.`test`}";
145        let (_, parsed) = Atomic::parse(template).unwrap();
146        let variable_chain = vec![
147            Variable::Simple("hello".to_string()),
148            Variable::Backquote("test".to_string()),
149        ];
150        assert_eq!(
151            parsed,
152            Atomic::Maybe(MaybeValue::Placeholder(Placeholder::Hash(
153                VariableChain::new(variable_chain.clone())
154            )))
155        );
156
157        let template = "'hello.`test`'";
158        let (_, parsed) = Atomic::parse(template).unwrap();
159        assert_eq!(
160            parsed,
161            Atomic::Text(Text::SingleQuote("'hello.`test`'".to_string()))
162        );
163
164        let template = "\"hello\"";
165        let (_, parsed) = Atomic::parse(template).unwrap();
166        let variable_chain = vec![Variable::DoubleQuote("hello".to_string())];
167        assert_eq!(
168            parsed,
169            Atomic::Maybe(MaybeValue::VariableChain(VariableChain::new(
170                variable_chain.clone()
171            )))
172        );
173
174        let template = r#"
175        {% for item in items %}
176            Item: {{ item | upper }}
177        {% endfor %}"#;
178        let (remaining, parsed) = Atomic::parse(template).unwrap();
179        let expected = TemplatePart::ControlBlock {
180            start_block: StartBlock {
181                name: "for".to_string(),
182                start_modifier: None,
183                end_modifier: None,
184                expr: "item in items".to_string(),
185            },
186            content: "Item: {{ item | upper }}".to_string(),
187            end_block: EndBlock {
188                name: "endfor".to_string(),
189                start_modifier: None,
190                end_modifier: None,
191            },
192        };
193        assert_eq!(parsed, Atomic::Maybe(MaybeValue::TemplatePart(expected)));
194    }
195
196    #[test]
197    fn atomic_parser_spec_002() {
198        let template = "¥";
199        let (_, parsed) = Atomic::parse(template).unwrap();
200        assert_eq!(parsed, Atomic::Sign(Sign::Unknown('¥')));
201    }
202
203    #[test]
204    fn atomic_parser_spec_003() {
205        let template = "1234";
206        let (_, parsed) = Atomic::parse(template).unwrap();
207        assert_eq!(
208            parsed,
209            Atomic::Number(Number {
210                value: "1234".to_string(),
211                unary_op: None
212            })
213        );
214    }
215}