1use nom::{
9 branch::alt,
10 bytes::complete::{tag, tag_no_case},
11 character::complete::multispace0,
12 combinator::{map, opt},
13 multi::separated_list0,
14 IResult,
15};
16
17use crate::common::{
18 parse_rust_flavored_variable, parse_string_escaped_rust_flavored_variable, parse_value, Column,
19 Value,
20};
21
22pub fn parse_comparisons(input: &str) -> IResult<&str, Vec<WhereClause>> {
26 let (input, _) = opt(tag_no_case("and"))(input)?;
28 let (input, _) = multispace0(input)?;
29
30 separated_list0(tag_no_case("and"), parse_where_condition)(input)
31}
32
33pub fn parse_where_clause(input: &str) -> IResult<&str, Vec<WhereClause>> {
42 let (input, _) = tag_no_case("where ")(input)?;
43
44 separated_list0(tag_no_case("and"), parse_where_condition)(input)
45}
46
47#[derive(Debug, PartialEq)]
49pub struct WhereClause {
50 pub column: Column,
52 pub operator: ComparisonOperator,
54 pub value: Value,
56}
57
58#[derive(Debug, PartialEq)]
60pub enum ComparisonOperator {
61 Equal,
63 GreaterThan,
65 LessThan,
67 GreaterThanOrEqual,
69 LessThanOrEqual,
71
72 In,
74 Contains,
76 ContainsKey,
78}
79
80fn parse_where_column(input: &str) -> IResult<&str, String> {
82 let (input, col) = alt((
83 map(parse_string_escaped_rust_flavored_variable, |x| {
84 format!("\"{x}\"")
85 }),
86 map(parse_rust_flavored_variable, |x: &str| x.to_string()),
87 ))(input)?;
88
89 Ok((input, col.clone()))
90}
91
92pub fn parse_where_condition(input: &str) -> IResult<&str, WhereClause> {
94 let (input, _) = multispace0(input)?;
96
97 let (input, column) = parse_where_column(input)?;
98 let (input, _) = multispace0(input)?;
99 let (input, operator) = parse_comparison_operator(input)?;
100 let (input, _) = multispace0(input)?;
101 let (input, value) = parse_value(input)?;
102
103 let (input, _) = multispace0(input)?;
105 let (input, _) = opt(tag(";"))(input)?;
107
108 Ok((
109 input,
110 WhereClause {
111 column: Column::Identifier(column),
112 operator,
113 value,
114 },
115 ))
116}
117
118fn parse_comparison_operator(input: &str) -> IResult<&str, ComparisonOperator> {
120 alt((
121 map(tag(">="), |_| ComparisonOperator::GreaterThanOrEqual),
122 map(tag("<="), |_| ComparisonOperator::LessThanOrEqual),
123 map(tag("="), |_| ComparisonOperator::Equal),
124 map(tag(">"), |_| ComparisonOperator::GreaterThan),
125 map(tag("<"), |_| ComparisonOperator::LessThan),
126 map(tag_no_case("in"), |_| ComparisonOperator::In),
127 map(tag_no_case("contains key"), |_| {
128 ComparisonOperator::ContainsKey
129 }),
130 map(tag_no_case("contains"), |_| ComparisonOperator::Contains),
131 ))(input)
132}
133
134#[cfg(test)]
135mod test {
136 use super::*;
137 use crate::*;
138 use pretty_assertions::assert_eq;
139
140 #[test]
141 fn test_funky_casing() {
142 assert_eq!(
143 parse_where_clause(
144 r#"where "userId" = ? and "actionOperation" = ? and "timeBucket" = ?"#
145 ),
146 Ok((
147 "",
148 vec![
149 WhereClause {
150 column: Column::Identifier(r#""userId""#.to_string()),
151 operator: ComparisonOperator::Equal,
152 value: Value::Variable(Variable::Placeholder)
153 },
154 WhereClause {
155 column: Column::Identifier(r#""actionOperation""#.to_string()),
156 operator: ComparisonOperator::Equal,
157 value: Value::Variable(Variable::Placeholder)
158 },
159 WhereClause {
160 column: Column::Identifier(r#""timeBucket""#.to_string()),
161 operator: ComparisonOperator::Equal,
162 value: Value::Variable(Variable::Placeholder)
163 }
164 ]
165 ))
166 );
167 }
168
169 #[test]
170 fn test_parse_single_where_clause() {
171 assert_eq!(
172 parse_where_clause("where id = ?"),
173 Ok((
174 "",
175 vec![WhereClause {
176 column: Column::Identifier("id".to_string()),
177 operator: ComparisonOperator::Equal,
178 value: Value::Variable(Variable::Placeholder)
179 }]
180 ))
181 );
182 }
183
184 #[test]
185 fn test_parse_multiple_where_clauses() {
186 assert_eq!(
187 parse_where_clause("where id = ? and name > :name"),
188 Ok((
189 "",
190 vec![
191 WhereClause {
192 column: Column::Identifier("id".to_string()),
193 operator: ComparisonOperator::Equal,
194 value: Value::Variable(Variable::Placeholder)
195 },
196 WhereClause {
197 column: Column::Identifier("name".to_string()),
198 operator: ComparisonOperator::GreaterThan,
199 value: Value::Variable(Variable::NamedVariable("name".to_string()))
200 }
201 ]
202 ))
203 );
204
205 assert_eq!(
206 parse_where_clause("where id = :id and name = :name and age > 10"),
207 Ok((
208 "",
209 vec![
210 WhereClause {
211 column: Column::Identifier("id".to_string()),
212 operator: ComparisonOperator::Equal,
213 value: Value::Variable(Variable::NamedVariable("id".to_string()))
214 },
215 WhereClause {
216 column: Column::Identifier("name".to_string()),
217 operator: ComparisonOperator::Equal,
218 value: Value::Variable(Variable::NamedVariable("name".to_string()))
219 },
220 WhereClause {
221 column: Column::Identifier("age".to_string()),
222 operator: ComparisonOperator::GreaterThan,
223 value: Value::Number(10)
224 }
225 ]
226 ))
227 );
228 }
229
230 #[test]
231 fn test_parse_comparison_operator() {
232 assert_eq!(
233 parse_comparison_operator("="),
234 Ok(("", ComparisonOperator::Equal))
235 );
236 assert_eq!(
237 parse_comparison_operator(">"),
238 Ok(("", ComparisonOperator::GreaterThan))
239 );
240 assert_eq!(
241 parse_comparison_operator("<"),
242 Ok(("", ComparisonOperator::LessThan))
243 );
244 assert_eq!(
245 parse_comparison_operator(">="),
246 Ok(("", ComparisonOperator::GreaterThanOrEqual))
247 );
248 assert_eq!(
249 parse_comparison_operator("<="),
250 Ok(("", ComparisonOperator::LessThanOrEqual))
251 );
252 assert_eq!(
253 parse_comparison_operator("in"),
254 Ok(("", ComparisonOperator::In))
255 );
256 assert_eq!(
257 parse_comparison_operator("contains"),
258 Ok(("", ComparisonOperator::Contains))
259 );
260 assert_eq!(
261 parse_comparison_operator("contains key"),
262 Ok(("", ComparisonOperator::ContainsKey))
263 );
264 }
265}